@usine/orchestrator 0.2.1 → 0.4.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 (222) hide show
  1. package/LICENSE +661 -21
  2. package/README.md +174 -52
  3. package/dist/board/items.d.ts +22 -3
  4. package/dist/board/items.d.ts.map +1 -1
  5. package/dist/board/items.js +44 -4
  6. package/dist/board/items.js.map +1 -1
  7. package/dist/board/projection.d.ts +8 -1
  8. package/dist/board/projection.d.ts.map +1 -1
  9. package/dist/board/projection.js +63 -14
  10. package/dist/board/projection.js.map +1 -1
  11. package/dist/board/tasks.d.ts +15 -3
  12. package/dist/board/tasks.d.ts.map +1 -1
  13. package/dist/board/tasks.js +28 -9
  14. package/dist/board/tasks.js.map +1 -1
  15. package/dist/cloudflare.d.ts +32 -0
  16. package/dist/cloudflare.d.ts.map +1 -0
  17. package/dist/cloudflare.js +68 -0
  18. package/dist/cloudflare.js.map +1 -0
  19. package/dist/credentials.d.ts +5 -6
  20. package/dist/credentials.d.ts.map +1 -1
  21. package/dist/credentials.js +22 -15
  22. package/dist/credentials.js.map +1 -1
  23. package/dist/current-instance/changes.d.ts +87 -0
  24. package/dist/current-instance/changes.d.ts.map +1 -0
  25. package/dist/current-instance/changes.js +141 -0
  26. package/dist/current-instance/changes.js.map +1 -0
  27. package/dist/current-instance/current-instance.d.ts +89 -0
  28. package/dist/current-instance/current-instance.d.ts.map +1 -0
  29. package/dist/current-instance/current-instance.js +163 -0
  30. package/dist/current-instance/current-instance.js.map +1 -0
  31. package/dist/exe-dev.d.ts +6 -2
  32. package/dist/exe-dev.d.ts.map +1 -1
  33. package/dist/exe-dev.js +10 -9
  34. package/dist/exe-dev.js.map +1 -1
  35. package/dist/http-client.d.ts +8 -0
  36. package/dist/http-client.d.ts.map +1 -0
  37. package/dist/http-client.js +8 -0
  38. package/dist/http-client.js.map +1 -0
  39. package/dist/idle-triggers.d.ts +1 -1
  40. package/dist/idle-triggers.d.ts.map +1 -1
  41. package/dist/idle-triggers.js +1 -1
  42. package/dist/idle-triggers.js.map +1 -1
  43. package/dist/label-reconciler.d.ts +5 -2
  44. package/dist/label-reconciler.d.ts.map +1 -1
  45. package/dist/label-reconciler.js.map +1 -1
  46. package/dist/lease-order.d.ts +60 -0
  47. package/dist/lease-order.d.ts.map +1 -0
  48. package/dist/lease-order.js +76 -0
  49. package/dist/lease-order.js.map +1 -0
  50. package/dist/local.d.ts +4 -18
  51. package/dist/local.d.ts.map +1 -1
  52. package/dist/local.js +9 -45
  53. package/dist/local.js.map +1 -1
  54. package/dist/loop-matches.d.ts +28 -0
  55. package/dist/loop-matches.d.ts.map +1 -0
  56. package/dist/loop-matches.js +74 -0
  57. package/dist/loop-matches.js.map +1 -0
  58. package/dist/operator/attempt.d.ts +76 -10
  59. package/dist/operator/attempt.d.ts.map +1 -1
  60. package/dist/operator/attempt.js +69 -11
  61. package/dist/operator/attempt.js.map +1 -1
  62. package/dist/operator/board.d.ts +48 -16
  63. package/dist/operator/board.d.ts.map +1 -1
  64. package/dist/operator/board.js +48 -17
  65. package/dist/operator/board.js.map +1 -1
  66. package/dist/operator/configuration.d.ts +314 -0
  67. package/dist/operator/configuration.d.ts.map +1 -0
  68. package/dist/operator/configuration.js +490 -0
  69. package/dist/operator/configuration.js.map +1 -0
  70. package/dist/operator/desk.d.ts +206 -0
  71. package/dist/operator/desk.d.ts.map +1 -0
  72. package/dist/operator/desk.js +525 -0
  73. package/dist/operator/desk.js.map +1 -0
  74. package/dist/operator/github-changes.d.ts +88 -0
  75. package/dist/operator/github-changes.d.ts.map +1 -0
  76. package/dist/operator/github-changes.js +266 -0
  77. package/dist/operator/github-changes.js.map +1 -0
  78. package/dist/operator/http.d.ts +135 -105
  79. package/dist/operator/http.d.ts.map +1 -1
  80. package/dist/operator/http.js +155 -30
  81. package/dist/operator/http.js.map +1 -1
  82. package/dist/operator/machines.d.ts +71 -0
  83. package/dist/operator/machines.d.ts.map +1 -0
  84. package/dist/operator/machines.js +103 -0
  85. package/dist/operator/machines.js.map +1 -0
  86. package/dist/operator/need.d.ts +58 -11
  87. package/dist/operator/need.d.ts.map +1 -1
  88. package/dist/operator/need.js +61 -9
  89. package/dist/operator/need.js.map +1 -1
  90. package/dist/operator/operator.d.ts +135 -29
  91. package/dist/operator/operator.d.ts.map +1 -1
  92. package/dist/operator/operator.js +370 -63
  93. package/dist/operator/operator.js.map +1 -1
  94. package/dist/operator/overview.d.ts +7 -7
  95. package/dist/operator/overview.d.ts.map +1 -1
  96. package/dist/operator/overview.js +5 -5
  97. package/dist/operator/overview.js.map +1 -1
  98. package/dist/operator/projection.d.ts +56 -5
  99. package/dist/operator/projection.d.ts.map +1 -1
  100. package/dist/operator/projection.js +108 -9
  101. package/dist/operator/projection.js.map +1 -1
  102. package/dist/operator/run.d.ts +101 -6
  103. package/dist/operator/run.d.ts.map +1 -1
  104. package/dist/operator/run.js +100 -7
  105. package/dist/operator/run.js.map +1 -1
  106. package/dist/operator/statistics/cache.d.ts +7 -0
  107. package/dist/operator/statistics/cache.d.ts.map +1 -0
  108. package/dist/operator/statistics/cache.js +40 -0
  109. package/dist/operator/statistics/cache.js.map +1 -0
  110. package/dist/operator/statistics/runs.d.ts +36 -0
  111. package/dist/operator/statistics/runs.d.ts.map +1 -0
  112. package/dist/operator/statistics/runs.js +284 -0
  113. package/dist/operator/statistics/runs.js.map +1 -0
  114. package/dist/operator/statistics/statistics.d.ts +20 -0
  115. package/dist/operator/statistics/statistics.d.ts.map +1 -0
  116. package/dist/operator/statistics/statistics.js +138 -0
  117. package/dist/operator/statistics/statistics.js.map +1 -0
  118. package/dist/operator/statistics/tasks.d.ts +30 -0
  119. package/dist/operator/statistics/tasks.d.ts.map +1 -0
  120. package/dist/operator/statistics/tasks.js +244 -0
  121. package/dist/operator/statistics/tasks.js.map +1 -0
  122. package/dist/operator/statistics/view.d.ts +302 -0
  123. package/dist/operator/statistics/view.d.ts.map +1 -0
  124. package/dist/operator/statistics/view.js +243 -0
  125. package/dist/operator/statistics/view.js.map +1 -0
  126. package/dist/operator/statistics/window.d.ts +24 -0
  127. package/dist/operator/statistics/window.d.ts.map +1 -0
  128. package/dist/operator/statistics/window.js +47 -0
  129. package/dist/operator/statistics/window.js.map +1 -0
  130. package/dist/operator/task.d.ts +94 -0
  131. package/dist/operator/task.d.ts.map +1 -0
  132. package/dist/operator/task.js +204 -0
  133. package/dist/operator/task.js.map +1 -0
  134. package/dist/operator/transcript.d.ts +12 -12
  135. package/dist/operator/transcript.d.ts.map +1 -1
  136. package/dist/operator/transcript.js +11 -9
  137. package/dist/operator/transcript.js.map +1 -1
  138. package/dist/operator/usage.d.ts +103 -0
  139. package/dist/operator/usage.d.ts.map +1 -0
  140. package/dist/operator/usage.js +234 -0
  141. package/dist/operator/usage.js.map +1 -0
  142. package/dist/orchestrator.d.ts +8 -5
  143. package/dist/orchestrator.d.ts.map +1 -1
  144. package/dist/orchestrator.js +4 -2
  145. package/dist/orchestrator.js.map +1 -1
  146. package/dist/polling/closed-item-asks.d.ts +8 -0
  147. package/dist/polling/closed-item-asks.d.ts.map +1 -0
  148. package/dist/polling/closed-item-asks.js +28 -0
  149. package/dist/polling/closed-item-asks.js.map +1 -0
  150. package/dist/polling/cycle.d.ts +28 -10
  151. package/dist/polling/cycle.d.ts.map +1 -1
  152. package/dist/polling/cycle.js +20 -6
  153. package/dist/polling/cycle.js.map +1 -1
  154. package/dist/polling/evaluation-failures.d.ts +42 -0
  155. package/dist/polling/evaluation-failures.d.ts.map +1 -0
  156. package/dist/polling/evaluation-failures.js +103 -0
  157. package/dist/polling/evaluation-failures.js.map +1 -0
  158. package/dist/polling/hints.d.ts +2 -2
  159. package/dist/polling/hints.d.ts.map +1 -1
  160. package/dist/polling/hints.js +2 -2
  161. package/dist/polling/hints.js.map +1 -1
  162. package/dist/polling/loop-evaluation.d.ts +21 -11
  163. package/dist/polling/loop-evaluation.d.ts.map +1 -1
  164. package/dist/polling/loop-evaluation.js +70 -27
  165. package/dist/polling/loop-evaluation.js.map +1 -1
  166. package/dist/polling/open-issues.d.ts +23 -0
  167. package/dist/polling/open-issues.d.ts.map +1 -0
  168. package/dist/polling/open-issues.js +77 -0
  169. package/dist/polling/open-issues.js.map +1 -0
  170. package/dist/polling/open-pull-requests.d.ts +28 -0
  171. package/dist/polling/open-pull-requests.d.ts.map +1 -0
  172. package/dist/polling/open-pull-requests.js +131 -0
  173. package/dist/polling/open-pull-requests.js.map +1 -0
  174. package/dist/polling/polling.d.ts +20 -11
  175. package/dist/polling/polling.d.ts.map +1 -1
  176. package/dist/polling/polling.js +215 -52
  177. package/dist/polling/polling.js.map +1 -1
  178. package/dist/polling/repository-sync.d.ts +21 -14
  179. package/dist/polling/repository-sync.d.ts.map +1 -1
  180. package/dist/polling/repository-sync.js +35 -28
  181. package/dist/polling/repository-sync.js.map +1 -1
  182. package/dist/polling-triggers.d.ts +1 -1
  183. package/dist/polling-triggers.d.ts.map +1 -1
  184. package/dist/polling-triggers.js +1 -1
  185. package/dist/polling-triggers.js.map +1 -1
  186. package/dist/reloads.d.ts +14 -0
  187. package/dist/reloads.d.ts.map +1 -0
  188. package/dist/reloads.js +7 -0
  189. package/dist/reloads.js.map +1 -0
  190. package/dist/runtime.d.ts +24 -0
  191. package/dist/runtime.d.ts.map +1 -0
  192. package/dist/runtime.js +58 -0
  193. package/dist/runtime.js.map +1 -0
  194. package/dist/schedule-triggers.d.ts +14 -10
  195. package/dist/schedule-triggers.d.ts.map +1 -1
  196. package/dist/schedule-triggers.js +36 -31
  197. package/dist/schedule-triggers.js.map +1 -1
  198. package/dist/scheduler.d.ts +45 -5
  199. package/dist/scheduler.d.ts.map +1 -1
  200. package/dist/scheduler.js +587 -44
  201. package/dist/scheduler.js.map +1 -1
  202. package/dist/ssh.d.ts +103 -0
  203. package/dist/ssh.d.ts.map +1 -0
  204. package/dist/ssh.js +144 -0
  205. package/dist/ssh.js.map +1 -0
  206. package/dist/trigger-condition.d.ts +12 -0
  207. package/dist/trigger-condition.d.ts.map +1 -0
  208. package/dist/trigger-condition.js +88 -0
  209. package/dist/trigger-condition.js.map +1 -0
  210. package/dist/trigger-submission.d.ts +16 -13
  211. package/dist/trigger-submission.d.ts.map +1 -1
  212. package/dist/trigger-submission.js +19 -14
  213. package/dist/trigger-submission.js.map +1 -1
  214. package/dist/worker-engine.d.ts +13 -0
  215. package/dist/worker-engine.d.ts.map +1 -0
  216. package/dist/worker-engine.js +56 -0
  217. package/dist/worker-engine.js.map +1 -0
  218. package/dist/worker-status.d.ts +140 -0
  219. package/dist/worker-status.d.ts.map +1 -0
  220. package/dist/worker-status.js +76 -0
  221. package/dist/worker-status.js.map +1 -0
  222. package/package.json +47 -22
@@ -0,0 +1,243 @@
1
+ import { AgentName } from "@usine/core/agent";
2
+ import { BrainSpec } from "@usine/core/agent/brain";
3
+ import { BillingPool, TokenCount, UsdAmount } from "@usine/core/agent/session";
4
+ import { ItemNumber } from "@usine/core/item";
5
+ import { LoopName } from "@usine/core/loop";
6
+ import { RepositoryName } from "@usine/core/repository";
7
+ import { WorkerName } from "@usine/core/worker";
8
+ import { Schema } from "effect";
9
+ const Count = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
10
+ const PositiveCount = Schema.Int.check(Schema.isGreaterThanOrEqualTo(1));
11
+ const Milliseconds = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
12
+ const Share = Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 1 }));
13
+ /** A UTC calendar day, such as `2026-10-25`. */
14
+ const UtcDay = Schema.String.check(Schema.isPattern(/^\d{4}-\d{2}-\d{2}$/u, { message: "Expected a UTC day as YYYY-MM-DD" }));
15
+ /** The span the statistics cover: today and the 6 or 29 UTC days before it, or every stored Run. */
16
+ export const StatisticsWindow = Schema.Literals(["7d", "30d", "all"]);
17
+ /** The length of the periods in a time series: a UTC day, or a UTC week from Monday. */
18
+ export const StatisticsBucket = Schema.Literals(["day", "week"]);
19
+ /**
20
+ * A task whose pull request merged in the window. `durationMillis` runs from the opening of the
21
+ * task's first Run to the close of its last Run, or to the merge if a Run was open at the merge or
22
+ * closed after it. The wait between the last Run and the merge is left out.
23
+ */
24
+ export class TaskDoneView extends Schema.Class("@usine/orchestrator/TaskDoneView")({
25
+ repository: RepositoryName,
26
+ /** The task's first issue, or its first pull request if it has no issue. */
27
+ number: ItemNumber,
28
+ mergedAt: Schema.String,
29
+ durationMillis: Milliseconds,
30
+ }) {
31
+ }
32
+ /** The tasks done in one period. `medianMillis` is null if there are none. */
33
+ export class TaskPeriodView extends Schema.Class("@usine/orchestrator/TaskPeriodView")({
34
+ start: UtcDay,
35
+ tasks: Count,
36
+ medianMillis: Schema.NullOr(Milliseconds),
37
+ firstTimeRight: Count,
38
+ operatorActions: Count,
39
+ }) {
40
+ }
41
+ /** A Loop's share of the measured tasks' time or tokens, averaged over the tasks. */
42
+ export class LoopShareView extends Schema.Class("@usine/orchestrator/LoopShareView")({ loop: LoopName, share: Share }) {
43
+ }
44
+ /** The median wall time of a Loop's closed Runs on the measured tasks, summed over Attempts. */
45
+ export class LoopRunTimeView extends Schema.Class("@usine/orchestrator/LoopRunTimeView")({ loop: LoopName, runs: PositiveCount, medianMillis: Milliseconds }) {
46
+ }
47
+ /**
48
+ * The time of the tasks done whose Attempts all have a recorded end. Each share is the part of a
49
+ * task's duration that the Loop's Attempts ran, or that no Attempt ran (`waiting`: approval, queue,
50
+ * and the time between Runs), averaged over the tasks.
51
+ */
52
+ export class TaskTimeView extends Schema.Class("@usine/orchestrator/TaskTimeView")({
53
+ tasks: PositiveCount,
54
+ loops: Schema.Array(LoopShareView),
55
+ waiting: Share,
56
+ runs: Schema.Array(LoopRunTimeView),
57
+ }) {
58
+ }
59
+ /**
60
+ * The median cost of the priced tasks: those whose sessions all have a price. `estimate` is true if
61
+ * a session of a priced task is in the Claude or ChatGPT plan.
62
+ */
63
+ export class TaskCostView extends Schema.Class("@usine/orchestrator/TaskCostView")({
64
+ tasks: PositiveCount,
65
+ medianUsd: UsdAmount,
66
+ estimate: Schema.Boolean,
67
+ }) {
68
+ }
69
+ /**
70
+ * The tokens of the tasks done whose Runs with an Attempt all reported tokens, with each Loop's
71
+ * share. `cost` is null if no such task is priced.
72
+ */
73
+ export class TaskTokensView extends Schema.Class("@usine/orchestrator/TaskTokensView")({
74
+ tasks: PositiveCount,
75
+ medianTokens: TokenCount,
76
+ loops: Schema.Array(LoopShareView),
77
+ cost: Schema.NullOr(TaskCostView),
78
+ }) {
79
+ }
80
+ /**
81
+ * The tasks done with an Attempt that ended before usine recorded end times, left out of `time`.
82
+ * Each such Attempt started before `before`.
83
+ */
84
+ export class UntimedTasksView extends Schema.Class("@usine/orchestrator/UntimedTasksView")({ tasks: PositiveCount, before: UtcDay }) {
85
+ }
86
+ /**
87
+ * The tasks done in `window`: a task is done when its pull request merges, and counts if it has a
88
+ * Run. Runs that a newer Event or an Item change canceled before an Attempt are left out.
89
+ */
90
+ export class StatisticsView extends Schema.Class("@usine/orchestrator/StatisticsView")({
91
+ window: StatisticsWindow,
92
+ /** The window's start: midnight UTC of its first day. */
93
+ from: Schema.String,
94
+ /** The time the statistics were computed. */
95
+ to: Schema.String,
96
+ bucket: StatisticsBucket,
97
+ /**
98
+ * The Loops of the window's tasks in the order they first run in a task: by mean rank, from 0
99
+ * for a task's first Loop to 1 for its last, then by name. `time` and `tokens` follow it.
100
+ */
101
+ loops: Schema.Array(LoopName),
102
+ /** By merge time, oldest first. */
103
+ tasks: Schema.Array(TaskDoneView),
104
+ /** The median of the tasks' `durationMillis`, or null without a task. */
105
+ medianMillis: Schema.NullOr(Milliseconds),
106
+ /** One period per bucket from `from` to `to`, oldest first. */
107
+ periods: Schema.Array(TaskPeriodView),
108
+ /** The tasks with no Loop that succeeded twice on one of their Items. */
109
+ firstTimeRight: Count,
110
+ /**
111
+ * The approvals, operator retries, failures marked resolved, and cycle-budget resets on the
112
+ * tasks' Runs and Items.
113
+ */
114
+ operatorActions: Count,
115
+ time: Schema.NullOr(TaskTimeView),
116
+ tokens: Schema.NullOr(TaskTokensView),
117
+ untimed: Schema.NullOr(UntimedTasksView),
118
+ /** The tasks with a Run that has an Attempt but no token report, left out of `tokens`. */
119
+ unmetered: Count,
120
+ }) {
121
+ }
122
+ /** The median of at least one duration. */
123
+ export class DurationSummaryView extends Schema.Class("@usine/orchestrator/DurationSummaryView")({ count: PositiveCount, medianMillis: Milliseconds }) {
124
+ }
125
+ /** Closed Runs or ended Attempts counted by outcome. */
126
+ export class OutcomeCountsView extends Schema.Class("@usine/orchestrator/OutcomeCountsView")({ succeeded: Count, failed: Count, canceled: Count }) {
127
+ }
128
+ /** The tokens used in one billing pool. */
129
+ export class PoolTokensView extends Schema.Class("@usine/orchestrator/PoolTokensView")({ pool: BillingPool, tokens: TokenCount }) {
130
+ }
131
+ /**
132
+ * The cost of the priced Runs or Attempts: those with a price for each session. `estimate` is true
133
+ * if a priced session is in the Claude or ChatGPT plan.
134
+ */
135
+ export class CostSummaryView extends Schema.Class("@usine/orchestrator/CostSummaryView")({
136
+ usd: UsdAmount,
137
+ meanUsd: UsdAmount,
138
+ estimate: Schema.Boolean,
139
+ priced: PositiveCount,
140
+ }) {
141
+ }
142
+ /**
143
+ * The tokens and cost of the Runs or Attempts with at least one Attempt. `meanTokens` is null if
144
+ * none has one, and `cost` is null if none is priced.
145
+ */
146
+ export class UsageSummaryView extends Schema.Class("@usine/orchestrator/UsageSummaryView")({
147
+ measured: Count,
148
+ tokens: TokenCount,
149
+ meanTokens: Schema.NullOr(TokenCount),
150
+ /** In the order API keys, Claude plan, ChatGPT plan; unused pools are absent. */
151
+ pools: Schema.Array(PoolTokensView),
152
+ cost: Schema.NullOr(CostSummaryView),
153
+ }) {
154
+ }
155
+ const groupFields = {
156
+ outcomes: OutcomeCountsView,
157
+ /** Run time for a Loop or Agent, Attempt time for a brain; null without a measured one. */
158
+ time: Schema.NullOr(DurationSummaryView),
159
+ usage: UsageSummaryView,
160
+ };
161
+ /** The closed Runs of one Loop. */
162
+ export class LoopStatisticsView extends Schema.Class("@usine/orchestrator/LoopStatisticsView")({ loop: LoopName, ...groupFields }) {
163
+ }
164
+ /** The closed Runs whose effective Agent is `agent`. */
165
+ export class AgentStatisticsView extends Schema.Class("@usine/orchestrator/AgentStatisticsView")({ agent: AgentName, ...groupFields }) {
166
+ }
167
+ /** The ended Attempts of closed Runs that ran with one harness, model, and effort. */
168
+ export class BrainStatisticsView extends Schema.Class("@usine/orchestrator/BrainStatisticsView")({ brain: BrainSpec, ...groupFields }) {
169
+ }
170
+ /** The Runs that closed in one period, by outcome. */
171
+ export class ThroughputPeriodView extends Schema.Class("@usine/orchestrator/ThroughputPeriodView")({ start: UtcDay, ...OutcomeCountsView.fields }) {
172
+ }
173
+ /** The time from a Run's opening, or its approval, to its first Attempt's start on `worker`. */
174
+ export class WorkerQueueWaitView extends Schema.Class("@usine/orchestrator/WorkerQueueWaitView")({ worker: WorkerName, wait: DurationSummaryView }) {
175
+ }
176
+ /** The number of closed Runs that took `attempts` Attempts. */
177
+ export class AttemptCountView extends Schema.Class("@usine/orchestrator/AttemptCountView")({ attempts: PositiveCount, runs: Count }) {
178
+ }
179
+ /**
180
+ * The time a declared Worker's slots held Attempts in the window, as a share of `slots` times the
181
+ * window's length.
182
+ */
183
+ export class WorkerUtilizationView extends Schema.Class("@usine/orchestrator/WorkerUtilizationView")({
184
+ worker: WorkerName,
185
+ slots: PositiveCount,
186
+ occupiedMillis: Milliseconds,
187
+ share: Share,
188
+ }) {
189
+ }
190
+ /** The Needs attention entries raised in one period. */
191
+ export class NeedsAttentionPeriodView extends Schema.Class("@usine/orchestrator/NeedsAttentionPeriodView")({ start: UtcDay, approvals: Count, failures: Count }) {
192
+ }
193
+ /**
194
+ * The approvals asked for and the failed Attempts that no automatic retry followed, with their
195
+ * waits: from a run's opening to its approval, and from a failure to its first recorded operator
196
+ * answer (retry or resolution). Each answered failure contributes once to `failureWait` and to
197
+ * either `retryWait` or `resolutionWait`. Older resolutions without a timestamp are excluded.
198
+ */
199
+ export class NeedsAttentionView extends Schema.Class("@usine/orchestrator/NeedsAttentionView")({
200
+ approvals: Count,
201
+ failures: Count,
202
+ periods: Schema.Array(NeedsAttentionPeriodView),
203
+ approvalWait: Schema.NullOr(DurationSummaryView),
204
+ retryWait: Schema.NullOr(DurationSummaryView),
205
+ resolutionWait: Schema.NullOr(DurationSummaryView),
206
+ failureWait: Schema.NullOr(DurationSummaryView),
207
+ }) {
208
+ }
209
+ /**
210
+ * How the Runs went in `window`: closed Runs by outcome, Loop, Agent, and brain, with their time,
211
+ * tokens, and cost, and the Worker, retry, and Needs attention measures. Runs that a newer Event or
212
+ * an Item change canceled before an Attempt are left out.
213
+ */
214
+ export class RunStatisticsView extends Schema.Class("@usine/orchestrator/RunStatisticsView")({
215
+ window: StatisticsWindow,
216
+ /** The window's start: midnight UTC of its first day. */
217
+ from: Schema.String,
218
+ /** The time the statistics were computed. */
219
+ to: Schema.String,
220
+ bucket: StatisticsBucket,
221
+ runs: OutcomeCountsView,
222
+ /** From each closed Run's opening to its close. */
223
+ runTime: Schema.NullOr(DurationSummaryView),
224
+ /** From each ended Attempt's start to its recorded end. */
225
+ attemptTime: Schema.NullOr(DurationSummaryView),
226
+ /** The ended Attempts closed before end times were recorded, left out of `attemptTime`. */
227
+ unmeasuredAttempts: Count,
228
+ /** One period per bucket from `from` to `to`, oldest first. */
229
+ throughput: Schema.Array(ThroughputPeriodView),
230
+ /** By closed Run count, highest first. */
231
+ loops: Schema.Array(LoopStatisticsView),
232
+ agents: Schema.Array(AgentStatisticsView),
233
+ brains: Schema.Array(BrainStatisticsView),
234
+ /** For Runs whose first Attempt started in the window, by Worker name. */
235
+ queueWait: Schema.Array(WorkerQueueWaitView),
236
+ /** From one Attempt to the most any closed Run took, each count present. */
237
+ attemptsPerRun: Schema.Array(AttemptCountView),
238
+ /** For the Workers that `usine.yaml` declares, in declaration order. */
239
+ utilization: Schema.Array(WorkerUtilizationView),
240
+ needsAttention: NeedsAttentionView,
241
+ }) {
242
+ }
243
+ //# sourceMappingURL=view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/operator/statistics/view.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { RunSummary } from "@usine/engine/run-reader";
2
+ import { DateTime, Option } from "effect";
3
+ import type { StatisticsBucket, StatisticsWindow } from "./view.ts";
4
+ export declare const DAY = 86400000;
5
+ export declare const epoch: (self: DateTime.DateTime) => number;
6
+ export declare const startOfUtcDay: (epochMillis: number) => number;
7
+ /** Returns the start of `window`, or `None` for `all`, whose start depends on the stored Runs. */
8
+ export declare const windowStart: (window: StatisticsWindow, now: DateTime.Utc) => Option.Option<DateTime.Utc>;
9
+ /** Returns the period length for a span: days up to 62 days, else weeks. */
10
+ export declare const spanBucket: (from: number, to: number) => StatisticsBucket;
11
+ export declare const utcDay: (epochMillis: number) => string;
12
+ /** Returns the start of the period that holds `epochMillis`. Weeks start on Monday. */
13
+ export declare const periodStart: (bucket: StatisticsBucket, epochMillis: number) => number;
14
+ /** Returns the start of each period from `from` to `to`, oldest first. */
15
+ export declare const periodStarts: (bucket: StatisticsBucket, from: number, to: number) => ReadonlyArray<number>;
16
+ export declare const sum: (values: ReadonlyArray<number>) => number;
17
+ /** Returns the median of `values`, the mean of the two middle values for an even count. */
18
+ export declare const median: (values: ReadonlyArray<number>) => Option.Option<number>;
19
+ /**
20
+ * Whether the engine canceled the Run before any Attempt because a newer Event or an Item change
21
+ * replaced it.
22
+ */
23
+ export declare const replacedBeforeStart: ({ run, attempts }: RunSummary) => boolean;
24
+ //# sourceMappingURL=window.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../../../src/operator/statistics/window.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAwB,QAAQ,EAAE,MAAM,EAAS,MAAM,QAAQ,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAEpE,eAAO,MAAM,GAAG,WAAa,CAAC;AAM9B,eAAO,MAAM,KAAK,qCAAyB,CAAC;AAE5C,eAAO,MAAM,aAAa,gBAAiB,MAAM,KAAG,MAA2C,CAAC;AAEhG,kGAAkG;AAClG,eAAO,MAAM,WAAW,WACd,gBAAgB,OACnB,QAAQ,CAAC,GAAG,KAChB,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAOtB,CAAC;AAER,4EAA4E;AAC5E,eAAO,MAAM,UAAU,SAAU,MAAM,MAAM,MAAM,KAAG,gBACoB,CAAC;AAE3E,eAAO,MAAM,MAAM,gBAAiB,MAAM,KAAG,MACK,CAAC;AAEnD,uFAAuF;AACvF,eAAO,MAAM,WAAW,WAAY,gBAAgB,eAAe,MAAM,KAAG,MAG3E,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,YAAY,WACf,gBAAgB,QAClB,MAAM,MACR,MAAM,KACT,aAAa,CAAC,MAAM,CAKtB,CAAC;AAEF,eAAO,MAAM,GAAG,WAAY,aAAa,CAAC,MAAM,CAAC,KAAG,MACD,CAAC;AAEpD,2FAA2F;AAC3F,eAAO,MAAM,MAAM,WAAY,aAAa,CAAC,MAAM,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAO1E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,sBAAuB,UAAU,KAAG,OAGtB,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { Array as EffectArray, DateTime, Option, Order } from "effect";
2
+ export const DAY = 86_400_000;
3
+ const WEEK = 7 * DAY;
4
+ /** The longest span, in days, that the time series still split into days. */
5
+ const DAILY_SPAN_DAYS = 62;
6
+ const WINDOW_DAYS = { "7d": 7, "30d": 30 };
7
+ export const epoch = DateTime.toEpochMillis;
8
+ export const startOfUtcDay = (epochMillis) => epochMillis - (epochMillis % DAY);
9
+ /** Returns the start of `window`, or `None` for `all`, whose start depends on the stored Runs. */
10
+ export const windowStart = (window, now) => window === "all"
11
+ ? Option.none()
12
+ : Option.some(DateTime.makeUnsafe(startOfUtcDay(DateTime.toEpochMillis(now)) - (WINDOW_DAYS[window] - 1) * DAY));
13
+ /** Returns the period length for a span: days up to 62 days, else weeks. */
14
+ export const spanBucket = (from, to) => (startOfUtcDay(to) - from) / DAY + 1 <= DAILY_SPAN_DAYS ? "day" : "week";
15
+ export const utcDay = (epochMillis) => new Date(epochMillis).toISOString().slice(0, 10);
16
+ /** Returns the start of the period that holds `epochMillis`. Weeks start on Monday. */
17
+ export const periodStart = (bucket, epochMillis) => {
18
+ const day = startOfUtcDay(epochMillis);
19
+ return bucket === "day" ? day : day - ((new Date(day).getUTCDay() + 6) % 7) * DAY;
20
+ };
21
+ /** Returns the start of each period from `from` to `to`, oldest first. */
22
+ export const periodStarts = (bucket, from, to) => {
23
+ const step = bucket === "day" ? DAY : WEEK;
24
+ const starts = [];
25
+ for (let start = periodStart(bucket, from); start <= to; start += step)
26
+ starts.push(start);
27
+ return starts;
28
+ };
29
+ export const sum = (values) => values.reduce((total, value) => total + value, 0);
30
+ /** Returns the median of `values`, the mean of the two middle values for an even count. */
31
+ export const median = (values) => {
32
+ if (!EffectArray.isReadonlyArrayNonEmpty(values))
33
+ return Option.none();
34
+ const sorted = EffectArray.sort(values, Order.Number);
35
+ const middle = (sorted.length - 1) / 2;
36
+ const lower = sorted[Math.floor(middle)] ?? sorted[0];
37
+ const upper = sorted[Math.ceil(middle)] ?? lower;
38
+ return Option.some((lower + upper) / 2);
39
+ };
40
+ /**
41
+ * Whether the engine canceled the Run before any Attempt because a newer Event or an Item change
42
+ * replaced it.
43
+ */
44
+ export const replacedBeforeStart = ({ run, attempts }) => attempts.length === 0 &&
45
+ run.state._tag === "Canceled" &&
46
+ run.state.reason._tag !== "OperatorCanceled";
47
+ //# sourceMappingURL=window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"window.js","sourceRoot":"","sources":["../../../src/operator/statistics/window.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ import type { LoadedInstance } from "@usine/config/instance";
2
+ import { ItemNumber } from "@usine/core/item";
3
+ import { RepositoryName } from "@usine/core/repository";
4
+ import { CycleBudgets } from "@usine/engine/cycle-budget";
5
+ import type { RunStoreError } from "@usine/engine/engine";
6
+ import { ItemProjectionReader } from "@usine/engine/item-projection";
7
+ import { LoopControl } from "@usine/engine/loop-control";
8
+ import { RepositoryLabelProjectionReader } from "@usine/engine/repository-label-projection";
9
+ import { RunReader } from "@usine/engine/run-reader";
10
+ import { Context, Effect, Layer, Schema } from "effect";
11
+ import { BoardItemNotFoundError, BoardProjectNotFoundError } from "./board.ts";
12
+ import { RunView } from "./run.ts";
13
+ declare const TaskItemView_base: Schema.Class<TaskItemView, Schema.Struct<{
14
+ readonly itemKey: Schema.brand<Schema.NonEmptyString, "ItemKey">;
15
+ readonly kind: Schema.Literals<readonly ["issue", "pr"]>;
16
+ readonly number: Schema.brand<Schema.Int, "ItemNumber">;
17
+ readonly title: Schema.String;
18
+ readonly url: Schema.NonEmptyString;
19
+ readonly state: Schema.Literals<readonly ["issue-open", "issue-closed", "pr-open", "pr-closed", "pr-merged"]>;
20
+ readonly labels: Schema.$Array<Schema.brand<Schema.String, "GitHubLabelName">>;
21
+ /** Whether the Item is an open pull request whose branch conflicts with its base. */
22
+ readonly conflicting: Schema.Boolean;
23
+ }>, {}>;
24
+ /** One issue or pull request of a Task, with its GitHub state as the Board card shows it. */
25
+ export declare class TaskItemView extends TaskItemView_base {
26
+ }
27
+ declare const TaskLaneView_base: Schema.Class<TaskLaneView, Schema.Struct<{
28
+ readonly id: Schema.brand<Schema.NonEmptyString, "BoardLaneId">;
29
+ readonly name: Schema.brand<Schema.String, "LaneName">;
30
+ }>, {}>;
31
+ /**
32
+ * The Task's Board lane: its current Item's lane or, while a Run runs and the Item has no lane
33
+ * label, the lane that Run was triggered from.
34
+ */
35
+ export declare class TaskLaneView extends TaskLaneView_base {
36
+ }
37
+ declare const TaskCommentView_base: Schema.Class<TaskCommentView, Schema.Struct<{
38
+ readonly runId: Schema.brand<Schema.NonEmptyString, "RunId">;
39
+ readonly kind: Schema.Literals<readonly ["issue-comment", "review", "review-comment"]>;
40
+ readonly url: Schema.String;
41
+ readonly postedAt: Schema.NullOr<Schema.String>;
42
+ readonly attemptEndedAt: Schema.NullOr<Schema.String>;
43
+ }>, {}>;
44
+ /**
45
+ * A comment or review an Attempt posted on its Run's Item. `postedAt` and `attemptEndedAt` are null
46
+ * for records made before those times were stored.
47
+ */
48
+ export declare class TaskCommentView extends TaskCommentView_base {
49
+ }
50
+ declare const TaskView_base: Schema.Class<TaskView, Schema.Struct<{
51
+ readonly repository: Schema.brand<Schema.String, "RepositoryName">;
52
+ readonly number: Schema.brand<Schema.Int, "ItemNumber">;
53
+ readonly title: Schema.String;
54
+ /**
55
+ * The Item whose labels and state set the Task's lane (unless a running Run keeps it in its
56
+ * trigger lane), and whose labels a Board move changes.
57
+ */
58
+ readonly current: Schema.brand<Schema.NonEmptyString, "ItemKey">;
59
+ readonly lane: typeof TaskLaneView;
60
+ /**
61
+ * Whether a Run on the Task's Items waits for approval or a failure decision, or an Item used up
62
+ * its cycle budget.
63
+ */
64
+ readonly needsYou: Schema.Boolean;
65
+ /** Issues first, then pull requests, each by number; the first is the originating Item. */
66
+ readonly items: Schema.NonEmptyArray<typeof TaskItemView>;
67
+ /** Every saved Run on the Task's Items, newest first. */
68
+ readonly runs: Schema.$Array<typeof RunView>;
69
+ /** The harvested comments and reviews of those Runs, newest Run first, and newest first within a Run. */
70
+ readonly comments: Schema.$Array<typeof TaskCommentView>;
71
+ }>, {}>;
72
+ /**
73
+ * One Task as its page shows it (ADR 0024). Tasks have no stored identity; `number` is the
74
+ * originating Item: the earliest issue, or else the earliest pull request.
75
+ */
76
+ export declare class TaskView extends TaskView_base {
77
+ }
78
+ export type TaskReadError = RunStoreError | BoardProjectNotFoundError | BoardItemNotFoundError;
79
+ declare const TaskReader_base: Context.ServiceClass<TaskReader, "@usine/orchestrator/TaskReader", {
80
+ /**
81
+ * Returns the Task that contains Item `item` of `repository`. Fails if the repository isn't a
82
+ * Project or the Item isn't among the Board's newest Items.
83
+ */
84
+ readonly read: (instance: LoadedInstance, repository: RepositoryName, item: ItemNumber) => Effect.Effect<TaskView, TaskReadError>;
85
+ }>;
86
+ /**
87
+ * Reads one Task from the Board projection over the Project's newest Items and the saved Runs of
88
+ * those Items.
89
+ */
90
+ export declare class TaskReader extends TaskReader_base {
91
+ static readonly layer: Layer.Layer<TaskReader, never, CycleBudgets | ItemProjectionReader | LoopControl | RepositoryLabelProjectionReader | RunReader>;
92
+ }
93
+ export {};
94
+ //# sourceMappingURL=task.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/operator/task.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EAAW,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAA2B,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGjF,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,+BAA+B,EAAE,MAAM,2CAA2C,CAAC;AAC5F,OAAO,EAAmB,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAEL,OAAO,EAEP,MAAM,EACN,KAAK,EAGL,MAAM,EACP,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAEL,sBAAsB,EAEtB,yBAAyB,EAC1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;;;;;;;;;IASjC,qFAAqF;;;AAJvF,6FAA6F;AAC7F,qBAAa,YAAa,SAAQ,iBAKhC;CAAG;;;;;AAEL;;;GAGG;AACH,qBAAa,YAAa,SAAQ,iBAGhC;CAAG;;;;;;;;AAEL;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,oBAQnC;CAAG;;;;;IAUH;;;OAGG;;;IAGH;;;OAGG;;IAEH,2FAA2F;;IAE3F,yDAAyD;;IAEzD,yGAAyG;;;AAvB3G;;;GAGG;AACH,qBAAa,QAAS,SAAQ,aAqB5B;CAAG;AAEL,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,yBAAyB,GAAG,sBAAsB,CAAC;;IAuL3F;;;OAGG;mBACY,CACb,QAAQ,EAAE,cAAc,EACxB,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,UAAU,KACb,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC;;AAf/C;;;GAGG;AACH,qBAAa,UAAW,SAAQ,eAaK;IACnC,MAAM,CAAC,QAAQ,CAAC,KAAK,kIAA4C;CAClE"}
@@ -0,0 +1,204 @@
1
+ import { LaneName } from "@usine/config/board";
2
+ import { GitHubLabelName } from "@usine/config/label";
3
+ import { ItemKey, ItemNumber } from "@usine/core/item";
4
+ import { canonicalRepositoryName, RepositoryName } from "@usine/core/repository";
5
+ import { RunId } from "@usine/core/run";
6
+ import { CommentKind } from "@usine/core/step/result";
7
+ import { CycleBudgets } from "@usine/engine/cycle-budget";
8
+ import { ItemProjectionReader } from "@usine/engine/item-projection";
9
+ import { LoopControl } from "@usine/engine/loop-control";
10
+ import { RepositoryLabelProjectionReader } from "@usine/engine/repository-label-projection";
11
+ import { RunReader } from "@usine/engine/run-reader";
12
+ import { Array as EffectArray, Context, DateTime, Effect, Layer, Option, Result, Schema, } from "effect";
13
+ import { projectedItem } from "../board/items.js";
14
+ import { projectBoard } from "../board/projection.js";
15
+ import { BoardItemLink, BoardItemNotFoundError, BoardLaneId, BoardProjectNotFoundError, } from "./board.js";
16
+ import { readRunningEvents, runView, workerAccesses } from "./projection.js";
17
+ import { RunView } from "./run.js";
18
+ /** The number of newest Items the Board considers for Task membership. */
19
+ const TASK_BOARD_ITEMS = 500;
20
+ /** One issue or pull request of a Task, with its GitHub state as the Board card shows it. */
21
+ export class TaskItemView extends Schema.Class("@usine/orchestrator/TaskItemView")({
22
+ ...BoardItemLink.fields,
23
+ labels: Schema.Array(GitHubLabelName),
24
+ /** Whether the Item is an open pull request whose branch conflicts with its base. */
25
+ conflicting: Schema.Boolean,
26
+ }) {
27
+ }
28
+ /**
29
+ * The Task's Board lane: its current Item's lane or, while a Run runs and the Item has no lane
30
+ * label, the lane that Run was triggered from.
31
+ */
32
+ export class TaskLaneView extends Schema.Class("@usine/orchestrator/TaskLaneView")({
33
+ id: BoardLaneId,
34
+ name: LaneName,
35
+ }) {
36
+ }
37
+ /**
38
+ * A comment or review an Attempt posted on its Run's Item. `postedAt` and `attemptEndedAt` are null
39
+ * for records made before those times were stored.
40
+ */
41
+ export class TaskCommentView extends Schema.Class("@usine/orchestrator/TaskCommentView")({
42
+ runId: RunId,
43
+ kind: CommentKind,
44
+ url: Schema.String,
45
+ postedAt: Schema.NullOr(Schema.String),
46
+ attemptEndedAt: Schema.NullOr(Schema.String),
47
+ }) {
48
+ }
49
+ /**
50
+ * One Task as its page shows it (ADR 0024). Tasks have no stored identity; `number` is the
51
+ * originating Item: the earliest issue, or else the earliest pull request.
52
+ */
53
+ export class TaskView extends Schema.Class("@usine/orchestrator/TaskView")({
54
+ repository: RepositoryName,
55
+ number: ItemNumber,
56
+ title: Schema.String,
57
+ /**
58
+ * The Item whose labels and state set the Task's lane (unless a running Run keeps it in its
59
+ * trigger lane), and whose labels a Board move changes.
60
+ */
61
+ current: ItemKey,
62
+ lane: TaskLaneView,
63
+ /**
64
+ * Whether a Run on the Task's Items waits for approval or a failure decision, or an Item used up
65
+ * its cycle budget.
66
+ */
67
+ needsYou: Schema.Boolean,
68
+ /** Issues first, then pull requests, each by number; the first is the originating Item. */
69
+ items: Schema.NonEmptyArray(TaskItemView),
70
+ /** Every saved Run on the Task's Items, newest first. */
71
+ runs: Schema.Array(RunView),
72
+ /** The harvested comments and reviews of those Runs, newest Run first, and newest first within a Run. */
73
+ comments: Schema.Array(TaskCommentView),
74
+ }) {
75
+ }
76
+ const isoOrNull = Option.match({ onNone: () => null, onSome: DateTime.formatIso });
77
+ /**
78
+ * Returns the comments and reviews that a Run's Attempts posted, newest first and without
79
+ * duplicates.
80
+ */
81
+ const harvestedComments = (summary) => {
82
+ const view = (comment, endedAt) => new TaskCommentView({
83
+ runId: summary.run.id,
84
+ kind: comment.kind,
85
+ url: comment.url.toString(),
86
+ postedAt: isoOrNull(comment.at),
87
+ attemptEndedAt: isoOrNull(endedAt),
88
+ });
89
+ const succeededEnd = EffectArray.findLast(summary.attempts, (attempt) => Option.exists(attempt.outcome, ({ _tag }) => _tag === "Succeeded")).pipe(Option.flatMap(({ endedAt }) => endedAt));
90
+ const delivered = Option.match(summary.step.result, {
91
+ onNone: () => [],
92
+ onSome: ({ facts }) => facts.flatMap((fact) => (fact._tag === "Comment" ? [view(fact, succeededEnd)] : [])),
93
+ });
94
+ const unsuccessful = summary.attempts.flatMap((attempt) => attempt.comments.map((comment) => view(comment, attempt.endedAt)));
95
+ const time = ({ postedAt, attemptEndedAt }) => postedAt ?? attemptEndedAt ?? "";
96
+ return EffectArray.dedupeWith([...delivered, ...unsuccessful].sort((a, b) => time(b).localeCompare(time(a))), (a, b) => a.url === b.url);
97
+ };
98
+ const makeTaskReader = Effect.gen(function* () {
99
+ const reader = yield* RunReader;
100
+ const controls = yield* LoopControl;
101
+ const itemReader = yield* ItemProjectionReader;
102
+ const repositoryLabels = yield* RepositoryLabelProjectionReader;
103
+ const budgets = yield* CycleBudgets;
104
+ const read = Effect.fn("TaskReader.read")(function* (instance, repository, number) {
105
+ yield* Effect.annotateCurrentSpan({ repository, item: number });
106
+ const managed = instance.config.repos.find(({ name }) => canonicalRepositoryName(name) === canonicalRepositoryName(repository));
107
+ if (managed === undefined) {
108
+ yield* Effect.logInfo("Task read refused; the repository is not a registered Project", {
109
+ repository,
110
+ item: number,
111
+ });
112
+ return yield* new BoardProjectNotFoundError({ repository });
113
+ }
114
+ const [stored, labelProjection, states, asks] = yield* Effect.all([
115
+ itemReader.list(managed.name, { limit: TASK_BOARD_ITEMS }),
116
+ repositoryLabels.get(managed.name),
117
+ controls.states,
118
+ budgets.asks(),
119
+ ], { concurrency: "unbounded" });
120
+ const summaries = yield* reader.forItems(stored.map(({ itemKey }) => itemKey));
121
+ const runEvents = yield* readRunningEvents(reader, summaries);
122
+ const paused = new Set(states.filter((state) => state.paused).map((state) => state.loop));
123
+ const access = workerAccesses(instance.config.workers);
124
+ const board = yield* projectBoard(managed, Option.match(labelProjection, {
125
+ onNone: () => [],
126
+ onSome: ({ labels }) => labels.map(({ name }) => name),
127
+ }), stored, summaries.map((summary) => runView(summary, paused, access)), asks.map(({ itemKey }) => itemKey), new Map(), new Map(), runEvents);
128
+ const placed = board.lanes
129
+ .flatMap((lane) => lane.items.map((task) => ({ lane, task })))
130
+ .find(({ task }) => task.items.some((member) => member.number === number));
131
+ if (placed === undefined) {
132
+ yield* Effect.logInfo("Task read found no Task; the Item is not among the Project's newest Board Items", { repository: managed.name, item: number, window: TASK_BOARD_ITEMS });
133
+ return yield* new BoardItemNotFoundError({ repository: managed.name, item: number });
134
+ }
135
+ const { lane, task } = placed;
136
+ const memberKeys = new Set(task.items.map(({ itemKey }) => itemKey));
137
+ const projected = yield* Effect.forEach(stored.filter(({ itemKey }) => memberKeys.has(itemKey)), (item) => projectedItem(item).pipe(Effect.result));
138
+ const details = new Map();
139
+ for (const result of projected) {
140
+ if (Result.isFailure(result)) {
141
+ yield* Effect.logError("Task Item could not be projected; the other Items are shown", {
142
+ repository: managed.name,
143
+ itemKey: result.failure.itemKey,
144
+ message: result.failure.message,
145
+ });
146
+ continue;
147
+ }
148
+ const { itemKey, kind, number: itemNumber, title, url, state, labels, conflicting, } = result.success;
149
+ details.set(itemKey, new TaskItemView({
150
+ itemKey,
151
+ kind,
152
+ number: itemNumber,
153
+ title,
154
+ url,
155
+ state,
156
+ labels,
157
+ conflicting,
158
+ }));
159
+ }
160
+ const [first, ...rest] = task.items.flatMap(({ itemKey }) => {
161
+ const detail = details.get(itemKey);
162
+ return detail === undefined ? [] : [detail];
163
+ });
164
+ if (first === undefined) {
165
+ return yield* new BoardItemNotFoundError({ repository: managed.name, item: number });
166
+ }
167
+ const taskRuns = new Set(task.runs.map(({ id }) => id));
168
+ const comments = summaries
169
+ .filter(({ run }) => taskRuns.has(run.id))
170
+ .sort((a, b) => DateTime.toEpochMillis(b.run.openedAt) - DateTime.toEpochMillis(a.run.openedAt) ||
171
+ b.run.id.localeCompare(a.run.id))
172
+ .flatMap(harvestedComments);
173
+ const view = new TaskView({
174
+ repository: managed.name,
175
+ number: first.number,
176
+ title: task.title,
177
+ current: task.itemKey,
178
+ lane: new TaskLaneView({ id: lane.id, name: lane.name }),
179
+ needsYou: task.needsYou,
180
+ items: [first, ...rest],
181
+ runs: [...task.runs].reverse(),
182
+ comments,
183
+ });
184
+ yield* Effect.logDebug("Read a Task", {
185
+ repository: managed.name,
186
+ item: number,
187
+ origin: view.number,
188
+ lane: lane.name,
189
+ items: view.items.length,
190
+ runs: view.runs.length,
191
+ comments: comments.length,
192
+ });
193
+ return view;
194
+ });
195
+ return { read };
196
+ });
197
+ /**
198
+ * Reads one Task from the Board projection over the Project's newest Items and the saved Runs of
199
+ * those Items.
200
+ */
201
+ export class TaskReader extends Context.Service()("@usine/orchestrator/TaskReader") {
202
+ static layer = Layer.effect(TaskReader, makeTaskReader);
203
+ }
204
+ //# sourceMappingURL=task.js.map