@usine/engine 0.2.0 → 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.
- package/README.md +109 -36
- package/dist/board-moves/board-moves.d.ts +20 -0
- package/dist/board-moves/board-moves.d.ts.map +1 -0
- package/dist/board-moves/board-moves.js +74 -0
- package/dist/board-moves/board-moves.js.map +1 -0
- package/dist/board-moves/model.d.ts +21 -0
- package/dist/board-moves/model.d.ts.map +1 -0
- package/dist/board-moves/model.js +22 -0
- package/dist/board-moves/model.js.map +1 -0
- package/dist/cursor/cursor.d.ts +2 -6
- package/dist/cursor/cursor.d.ts.map +1 -1
- package/dist/cursor/cursor.js +2 -6
- package/dist/cursor/cursor.js.map +1 -1
- package/dist/cycle-budget/cycle-budget.d.ts +6 -6
- package/dist/cycle-budget/cycle-budget.d.ts.map +1 -1
- package/dist/cycle-budget/cycle-budget.js +5 -5
- package/dist/cycle-budget/cycle-budget.js.map +1 -1
- package/dist/cycle-budget/model.d.ts +2 -2
- package/dist/cycle-budget/model.d.ts.map +1 -1
- package/dist/cycle-budget/model.js +2 -2
- package/dist/cycle-budget/model.js.map +1 -1
- package/dist/cycle-budget/queries.d.ts +1 -1
- package/dist/cycle-budget/queries.d.ts.map +1 -1
- package/dist/cycle-budget/queries.js +3 -3
- package/dist/cycle-budget/queries.js.map +1 -1
- package/dist/database/database.d.ts +1 -1
- package/dist/database/database.d.ts.map +1 -1
- package/dist/database/local-sqlite.d.ts +10 -8
- package/dist/database/local-sqlite.d.ts.map +1 -1
- package/dist/database/local-sqlite.js +16 -9
- package/dist/database/local-sqlite.js.map +1 -1
- package/dist/database/schema.d.ts +581 -22
- package/dist/database/schema.d.ts.map +1 -1
- package/dist/database/schema.js +104 -23
- package/dist/database/schema.js.map +1 -1
- package/dist/{workflow → engine}/admission.d.ts +2 -2
- package/dist/engine/admission.d.ts.map +1 -0
- package/dist/{workflow → engine}/admission.js +4 -4
- package/dist/engine/admission.js.map +1 -0
- package/dist/engine/checkout-recovery.d.ts +69 -0
- package/dist/engine/checkout-recovery.d.ts.map +1 -0
- package/dist/engine/checkout-recovery.js +181 -0
- package/dist/engine/checkout-recovery.js.map +1 -0
- package/dist/engine/engine.d.ts +57 -24
- package/dist/engine/engine.d.ts.map +1 -1
- package/dist/engine/engine.js +144 -120
- package/dist/engine/engine.js.map +1 -1
- package/dist/engine/error.d.ts +36 -9
- package/dist/engine/error.d.ts.map +1 -1
- package/dist/engine/error.js +35 -7
- package/dist/engine/error.js.map +1 -1
- package/dist/engine/lifecycle.d.ts +81 -28
- package/dist/engine/lifecycle.d.ts.map +1 -1
- package/dist/engine/lifecycle.js +83 -44
- package/dist/engine/lifecycle.js.map +1 -1
- package/dist/engine/retry.d.ts +16 -0
- package/dist/engine/retry.d.ts.map +1 -0
- package/dist/engine/retry.js +18 -0
- package/dist/engine/retry.js.map +1 -0
- package/dist/engine/run-again.d.ts +45 -0
- package/dist/engine/run-again.d.ts.map +1 -0
- package/dist/engine/run-again.js +56 -0
- package/dist/engine/run-again.js.map +1 -0
- package/dist/engine/run-driver.d.ts +54 -0
- package/dist/engine/run-driver.d.ts.map +1 -0
- package/dist/engine/run-driver.js +362 -0
- package/dist/engine/run-driver.js.map +1 -0
- package/dist/engine/shutdown.d.ts +3 -4
- package/dist/engine/shutdown.d.ts.map +1 -1
- package/dist/engine/shutdown.js +3 -4
- package/dist/engine/shutdown.js.map +1 -1
- package/dist/engine/submission.d.ts +31 -11
- package/dist/engine/submission.d.ts.map +1 -1
- package/dist/engine/submission.js +42 -12
- package/dist/engine/submission.js.map +1 -1
- package/dist/item-projection/item-projection.d.ts +12 -4
- package/dist/item-projection/item-projection.d.ts.map +1 -1
- package/dist/item-projection/item-projection.js +41 -6
- package/dist/item-projection/item-projection.js.map +1 -1
- package/dist/item-projection/model.d.ts +5 -1
- package/dist/item-projection/model.d.ts.map +1 -1
- package/dist/item-projection/model.js +6 -2
- package/dist/item-projection/model.js.map +1 -1
- package/dist/loop-control/loop-control.d.ts +5 -5
- package/dist/loop-control/loop-control.d.ts.map +1 -1
- package/dist/loop-control/loop-control.js +4 -4
- package/dist/loop-control/loop-control.js.map +1 -1
- package/dist/loop-control/model.d.ts +1 -1
- package/dist/loop-control/model.d.ts.map +1 -1
- package/dist/loop-control/model.js +1 -1
- package/dist/loop-control/model.js.map +1 -1
- package/dist/repository-label-projection/model.d.ts +2 -2
- package/dist/repository-label-projection/model.d.ts.map +1 -1
- package/dist/repository-label-projection/model.js +2 -2
- package/dist/repository-label-projection/model.js.map +1 -1
- package/dist/repository-label-projection/repository-label-projection.d.ts +1 -1
- package/dist/repository-label-projection/repository-label-projection.d.ts.map +1 -1
- package/dist/repository-label-projection/repository-label-projection.js +1 -1
- package/dist/repository-label-projection/repository-label-projection.js.map +1 -1
- package/dist/run-reader/model.d.ts +62 -4
- package/dist/run-reader/model.d.ts.map +1 -1
- package/dist/run-reader/model.js +66 -7
- package/dist/run-reader/model.js.map +1 -1
- package/dist/run-reader/run-reader.d.ts +19 -9
- package/dist/run-reader/run-reader.d.ts.map +1 -1
- package/dist/run-reader/run-reader.js +23 -7
- package/dist/run-reader/run-reader.js.map +1 -1
- package/dist/step-executor.d.ts +65 -9
- package/dist/step-executor.d.ts.map +1 -1
- package/dist/step-executor.js +10 -2
- package/dist/step-executor.js.map +1 -1
- package/dist/store/attempt-windows.d.ts +8 -0
- package/dist/store/attempt-windows.d.ts.map +1 -0
- package/dist/store/attempt-windows.js +53 -0
- package/dist/store/attempt-windows.js.map +1 -0
- package/dist/store/codec.d.ts +381 -42
- package/dist/store/codec.d.ts.map +1 -1
- package/dist/store/codec.js +59 -15
- package/dist/store/codec.js.map +1 -1
- package/dist/store/leftovers.d.ts +10 -0
- package/dist/store/leftovers.d.ts.map +1 -0
- package/dist/store/leftovers.js +43 -0
- package/dist/store/leftovers.js.map +1 -0
- package/dist/store/loop-items.d.ts +16 -0
- package/dist/store/loop-items.d.ts.map +1 -0
- package/dist/store/loop-items.js +185 -0
- package/dist/store/loop-items.js.map +1 -0
- package/dist/store/queries.d.ts +345 -37
- package/dist/store/queries.d.ts.map +1 -1
- package/dist/store/queries.js +333 -160
- package/dist/store/queries.js.map +1 -1
- package/dist/store/store.d.ts +91 -54
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +481 -190
- package/dist/store/store.js.map +1 -1
- package/dist/store/transcript.d.ts +9 -0
- package/dist/store/transcript.d.ts.map +1 -0
- package/dist/store/transcript.js +90 -0
- package/dist/store/transcript.js.map +1 -0
- package/dist/transcript.d.ts +41 -20
- package/dist/transcript.d.ts.map +1 -1
- package/dist/transcript.js +45 -25
- package/dist/transcript.js.map +1 -1
- package/dist/trigger-condition.d.ts +23 -0
- package/dist/trigger-condition.d.ts.map +1 -0
- package/dist/trigger-condition.js +8 -0
- package/dist/trigger-condition.js.map +1 -0
- package/drizzle/20260912111855_operator_intervention/migration.sql +42 -0
- package/drizzle/20260912111855_operator_intervention/snapshot.json +1461 -0
- package/drizzle/20260917071139_resolve_run_failure/migration.sql +1 -0
- package/drizzle/20260917071139_resolve_run_failure/snapshot.json +1471 -0
- package/drizzle/20260918032643_attempt_salvage/migration.sql +1 -0
- package/drizzle/20260918032643_attempt_salvage/snapshot.json +1481 -0
- package/drizzle/20261011153856_drop_workflow_tables/migration.sql +4 -0
- package/drizzle/20261011153856_drop_workflow_tables/snapshot.json +1433 -0
- package/drizzle/20261017155043_attempt_salvage_check/migration.sql +41 -0
- package/drizzle/20261017155043_attempt_salvage_check/snapshot.json +1421 -0
- package/drizzle/20261018162700_discard_attempt_leftovers/migration.sql +1 -0
- package/drizzle/20261018162700_discard_attempt_leftovers/snapshot.json +1431 -0
- package/drizzle/20261018172605_loop_items_and_reopenings/migration.sql +15 -0
- package/drizzle/20261018172605_loop_items_and_reopenings/snapshot.json +1574 -0
- package/drizzle/20261018180339_attempt_ended_at/migration.sql +1 -0
- package/drizzle/20261018180339_attempt_ended_at/snapshot.json +1584 -0
- package/drizzle/20261019104043_run_approval_source/migration.sql +15 -0
- package/drizzle/20261019104043_run_approval_source/snapshot.json +1610 -0
- package/drizzle/20261019133538_board_moves/migration.sql +14 -0
- package/drizzle/20261019133538_board_moves/snapshot.json +1731 -0
- package/drizzle/20261019170538_item_projection_gone/migration.sql +1 -0
- package/drizzle/20261019170538_item_projection_gone/snapshot.json +1741 -0
- package/drizzle/20261019180649_run_again_events/migration.sql +7 -0
- package/drizzle/20261019180649_run_again_events/snapshot.json +1804 -0
- package/drizzle/20261019185008_trigger_event_permissions/migration.sql +1 -0
- package/drizzle/20261019185008_trigger_event_permissions/snapshot.json +1814 -0
- package/drizzle/20261020103746_attempt_start/migration.sql +5 -0
- package/drizzle/20261020103746_attempt_start/snapshot.json +1844 -0
- package/drizzle/20261020125211_attempt_comments/migration.sql +1 -0
- package/drizzle/20261020125211_attempt_comments/snapshot.json +1854 -0
- package/drizzle/20261020191320_run_closed_at/migration.sql +6 -0
- package/drizzle/20261020191320_run_closed_at/snapshot.json +1864 -0
- package/drizzle/20261023091428_rename_loop_item_hold_to_suppression/migration.sql +2 -0
- package/drizzle/20261023091428_rename_loop_item_hold_to_suppression/snapshot.json +1867 -0
- package/package.json +15 -5
- package/dist/workflow/admission.d.ts.map +0 -1
- package/dist/workflow/admission.js.map +0 -1
- package/dist/workflow/command.d.ts +0 -13
- package/dist/workflow/command.d.ts.map +0 -1
- package/dist/workflow/command.js +0 -17
- package/dist/workflow/command.js.map +0 -1
- package/dist/workflow/workflow.d.ts +0 -18
- package/dist/workflow/workflow.d.ts.map +0 -1
- package/dist/workflow/workflow.js +0 -108
- package/dist/workflow/workflow.js.map +0 -1
package/dist/store/queries.js
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { RepositoryName } from "@usine/core/repository";
|
|
2
|
+
import { WorkerName } from "@usine/core/worker";
|
|
3
|
+
import { Attempt, AttemptId, AttemptContext, AttemptNumber } from "@usine/core/attempt";
|
|
3
4
|
import { AttemptOutcome } from "@usine/core/attempt/outcome";
|
|
5
|
+
import { AttemptSalvage, salvageRefFor } from "@usine/core/attempt/salvage";
|
|
4
6
|
import { Event, DedupKey, EventId } from "@usine/core/event";
|
|
5
7
|
import { ItemKey } from "@usine/core/item";
|
|
6
|
-
import { LoopName } from "@usine/core/loop";
|
|
8
|
+
import { LoopName, LoopPermissions } from "@usine/core/loop";
|
|
7
9
|
import { RunId } from "@usine/core/run";
|
|
8
10
|
import { StepName } from "@usine/core/step";
|
|
9
11
|
import * as RunState from "@usine/core/run/state";
|
|
10
12
|
import { StepResult } from "@usine/core/step/result";
|
|
11
|
-
import { and, asc, desc, eq,
|
|
13
|
+
import { and, asc, desc, eq, inArray, isNull, ne, notInArray, sql, } from "drizzle-orm";
|
|
12
14
|
import { alias } from "drizzle-orm/sqlite-core";
|
|
13
|
-
import { Array as EffectArray, DateTime, Effect,
|
|
14
|
-
import { approvals,
|
|
15
|
+
import { Array as EffectArray, DateTime, Effect, Option, Schema } from "effect";
|
|
16
|
+
import { approvals, attempts, events, ignoredEvents, itemCycleBudgets, loopItems, projectionRevision, runAgainEvents, runs, steps, triggerEvents, } from "../database/schema.js";
|
|
15
17
|
import { RunNotFoundError } from "../engine/error.js";
|
|
16
18
|
import { RunSnapshot, RunSummary } from "../run-reader/model.js";
|
|
17
19
|
import { Submission } from "../engine/submission.js";
|
|
18
|
-
import { TranscriptQuery } from "../transcript.js";
|
|
19
|
-
import { WORKFLOW_COMMAND_PRIORITY } from "../workflow/command.js";
|
|
20
20
|
import { cycleQueries } from "../cycle-budget/queries.js";
|
|
21
|
-
import { attemptFromRow, decodeAttemptRows, decodeDiscards,
|
|
21
|
+
import { approvalFromRow, attemptFromRow, decodeAttemptRows, decodeDiscards, decodeNextStarts, decodeEventRoutes, decodeProjectionRevision, decodeReopenedEvents, decodeReopenings, decodeRunEventRows, decodeRunIds, decodeRunRows, encodeAgent, encodeAttemptStart, encodeBrainSpec, encodeDateTime, encodeOutcome, encodeComments, encodeSalvage, encodePayload, encodeRunState, encodeStepResult, eventFromRow, runAgainFromRow, runEventFromRow, runFromRow, runSummaryFromRow, stepFromRow, submissionFromRow, } from "./codec.js";
|
|
22
|
+
import { loopItemQueries } from "./loop-items.js";
|
|
23
|
+
import { transcriptQueries } from "./transcript.js";
|
|
22
24
|
const newestRunFirst = (left, right) => DateTime.toEpochMillis(right.openedAt) - DateTime.toEpochMillis(left.openedAt) ||
|
|
23
25
|
right.runId.localeCompare(left.runId);
|
|
24
26
|
const eventRouteSelection = {
|
|
@@ -30,6 +32,7 @@ const eventRouteSelection = {
|
|
|
30
32
|
loop: triggerEvents.loopName,
|
|
31
33
|
step: triggerEvents.stepName,
|
|
32
34
|
approval: triggerEvents.approval,
|
|
35
|
+
permissions: triggerEvents.permissions,
|
|
33
36
|
cycleLimit: triggerEvents.cycleLimit,
|
|
34
37
|
cycleGeneration: triggerEvents.cycleGeneration,
|
|
35
38
|
repository: triggerEvents.repository,
|
|
@@ -38,13 +41,16 @@ const eventRouteSelection = {
|
|
|
38
41
|
agent: triggerEvents.agentJson,
|
|
39
42
|
parentModel: triggerEvents.parentModel,
|
|
40
43
|
};
|
|
44
|
+
const approvalEvents = alias(events, "approval_event");
|
|
41
45
|
const runSelection = {
|
|
42
46
|
itemKey: triggerEvents.itemKey,
|
|
43
47
|
runId: runs.runId,
|
|
44
48
|
eventId: runs.eventId,
|
|
45
49
|
loop: runs.loopName,
|
|
46
50
|
state: runs.stateJson,
|
|
51
|
+
resolvedFailureAttemptId: runs.resolvedFailureAttemptId,
|
|
47
52
|
openedAt: runs.openedAt,
|
|
53
|
+
closedAt: runs.closedAt,
|
|
48
54
|
repository: runs.repository,
|
|
49
55
|
revision: runs.revision,
|
|
50
56
|
task: runs.task,
|
|
@@ -52,19 +58,31 @@ const runSelection = {
|
|
|
52
58
|
parentModel: runs.parentModel,
|
|
53
59
|
stepName: steps.stepName,
|
|
54
60
|
result: steps.result,
|
|
61
|
+
approvedAt: approvalEvents.receivedAt,
|
|
62
|
+
approvalChannel: approvals.channel,
|
|
63
|
+
approver: approvals.approver,
|
|
64
|
+
againFrom: runAgainEvents.fromRunId,
|
|
65
|
+
againRequestedBy: runAgainEvents.requestedBy,
|
|
55
66
|
};
|
|
56
|
-
const attemptSelection = {
|
|
67
|
+
export const attemptSelection = {
|
|
57
68
|
attemptId: attempts.attemptId,
|
|
58
69
|
runId: attempts.runId,
|
|
59
70
|
stepName: attempts.stepName,
|
|
60
71
|
worker: attempts.workerName,
|
|
61
72
|
number: attempts.attemptNumber,
|
|
62
73
|
brain: attempts.brainJson,
|
|
74
|
+
task: attempts.task,
|
|
75
|
+
agent: attempts.agent,
|
|
76
|
+
operatorIntervened: attempts.operatorIntervened,
|
|
63
77
|
parentModel: attempts.parentModel,
|
|
64
78
|
startedAt: attempts.startedAt,
|
|
65
79
|
outcome: attempts.outcomeJson,
|
|
80
|
+
endedAt: attempts.endedAt,
|
|
81
|
+
salvage: attempts.salvageJson,
|
|
82
|
+
start: attempts.startJson,
|
|
83
|
+
reattachedAt: attempts.reattachedAt,
|
|
84
|
+
comments: attempts.commentsJson,
|
|
66
85
|
};
|
|
67
|
-
// The state partitions are owned by @usine/core; these predicates only borrow their tags.
|
|
68
86
|
const storedStateTag = sql `json_extract(${runs.stateJson}, '$._tag')`;
|
|
69
87
|
const openState = inArray(storedStateTag, [...RunState.OPEN_STATE_TAGS]);
|
|
70
88
|
const unstartedState = inArray(storedStateTag, [...RunState.UNSTARTED_STATE_TAGS]);
|
|
@@ -72,8 +90,7 @@ const unstartedState = inArray(storedStateTag, [...RunState.UNSTARTED_STATE_TAGS
|
|
|
72
90
|
// cannot emit the declaration (TS2883).
|
|
73
91
|
const storedAgent = (agent) => Schema.decodeUnknownSync(Schema.Json)(encodeAgent(agent));
|
|
74
92
|
export const queriesFor = (database) => {
|
|
75
|
-
//
|
|
76
|
-
// Workflow suspension before its final mutation is observable (ADR 0020).
|
|
93
|
+
// `.execute()` stays explicit (ADR 0020).
|
|
77
94
|
const eventRouteByDedup = Effect.fn("RunStoreQueries.eventRouteByDedup")(function* (dedupKey) {
|
|
78
95
|
const rows = yield* database
|
|
79
96
|
.select(eventRouteSelection)
|
|
@@ -93,15 +110,57 @@ export const queriesFor = (database) => {
|
|
|
93
110
|
.execute();
|
|
94
111
|
return yield* decodeEventRoutes(rows);
|
|
95
112
|
});
|
|
113
|
+
const selectRuns = () => database
|
|
114
|
+
.select(runSelection)
|
|
115
|
+
.from(runs)
|
|
116
|
+
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
117
|
+
.innerJoin(steps, eq(steps.runId, runs.runId))
|
|
118
|
+
.leftJoin(approvals, eq(approvals.runId, runs.runId))
|
|
119
|
+
.leftJoin(approvalEvents, eq(approvalEvents.eventId, approvals.eventId))
|
|
120
|
+
.leftJoin(runAgainEvents, eq(runAgainEvents.eventId, runs.eventId));
|
|
96
121
|
const runRows = Effect.fn("RunStoreQueries.runRows")(function* (runId) {
|
|
122
|
+
const rows = yield* selectRuns().where(eq(runs.runId, runId)).execute();
|
|
123
|
+
return yield* decodeRunRows(rows);
|
|
124
|
+
});
|
|
125
|
+
/** Returns the Attempts that match `filters` and whose leftovers weren't discarded. */
|
|
126
|
+
const recoveries = (...filters) => Effect.gen(function* () {
|
|
97
127
|
const rows = yield* database
|
|
98
|
-
.select(
|
|
99
|
-
.
|
|
128
|
+
.select({
|
|
129
|
+
attemptId: attempts.attemptId,
|
|
130
|
+
worker: attempts.workerName,
|
|
131
|
+
repository: runs.repository,
|
|
132
|
+
runId: attempts.runId,
|
|
133
|
+
number: attempts.attemptNumber,
|
|
134
|
+
permissions: triggerEvents.permissions,
|
|
135
|
+
outcome: sql `coalesce(json_extract(${attempts.outcomeJson}, '$._tag'), '')`,
|
|
136
|
+
})
|
|
137
|
+
.from(attempts)
|
|
138
|
+
.innerJoin(runs, eq(runs.runId, attempts.runId))
|
|
100
139
|
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
101
|
-
.
|
|
102
|
-
.where(eq(runs.runId, runId))
|
|
140
|
+
.where(and(isNull(attempts.leftoversDiscardedAt), ...filters))
|
|
103
141
|
.execute();
|
|
104
|
-
|
|
142
|
+
const decoded = yield* Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({
|
|
143
|
+
attemptId: AttemptId,
|
|
144
|
+
worker: WorkerName,
|
|
145
|
+
repository: RepositoryName,
|
|
146
|
+
runId: RunId,
|
|
147
|
+
number: AttemptNumber,
|
|
148
|
+
permissions: LoopPermissions,
|
|
149
|
+
outcome: Schema.String,
|
|
150
|
+
})))(rows);
|
|
151
|
+
return decoded.map(({ runId, number, outcome, ...attempt }) => ({
|
|
152
|
+
...attempt,
|
|
153
|
+
ref: salvageRefFor(runId, number),
|
|
154
|
+
succeeded: outcome === "Succeeded",
|
|
155
|
+
open: outcome === "",
|
|
156
|
+
}));
|
|
157
|
+
});
|
|
158
|
+
const salvageCandidates = Effect.fn("RunStoreQueries.salvageCandidates")(function* () {
|
|
159
|
+
return yield* recoveries();
|
|
160
|
+
});
|
|
161
|
+
const salvageCandidate = Effect.fn("RunStoreQueries.salvageCandidate")(function* (attemptId) {
|
|
162
|
+
yield* Effect.annotateCurrentSpan({ attemptId });
|
|
163
|
+
return EffectArray.head(yield* recoveries(eq(attempts.attemptId, attemptId)));
|
|
105
164
|
});
|
|
106
165
|
const attemptRows = Effect.fn("RunStoreQueries.attemptRows")(function* (runId) {
|
|
107
166
|
const rows = yield* database
|
|
@@ -129,17 +188,21 @@ export const queriesFor = (database) => {
|
|
|
129
188
|
run: runFromRow(storedRun),
|
|
130
189
|
step: stepFromRow(storedRun),
|
|
131
190
|
attempts: storedAttempts.map(attemptFromRow),
|
|
191
|
+
resolvedFailureAttemptId: Option.fromNullishOr(storedRun.resolvedFailureAttemptId),
|
|
192
|
+
closedAt: Option.fromNullishOr(storedRun.closedAt),
|
|
193
|
+
approval: approvalFromRow(storedRun),
|
|
194
|
+
runAgain: runAgainFromRow(storedRun),
|
|
132
195
|
repository: storedRun.repository,
|
|
133
196
|
revision: storedRun.revision,
|
|
134
197
|
task: storedRun.task,
|
|
135
198
|
agent: storedRun.agent,
|
|
136
199
|
parentModel: Option.fromNullishOr(storedRun.parentModel),
|
|
200
|
+
permissions: route.permissions,
|
|
137
201
|
});
|
|
138
202
|
});
|
|
139
203
|
const summariesWithAttempts = Effect.fn("RunStoreQueries.summariesWithAttempts")(function* (storedRuns) {
|
|
140
204
|
const runIds = storedRuns.map(({ runId }) => runId);
|
|
141
|
-
//
|
|
142
|
-
// long-lived Task with many Runs.
|
|
205
|
+
// Chunks keep each read below the driver's bind-parameter limit.
|
|
143
206
|
const storedAttempts = (yield* Effect.forEach(EffectArray.chunksOf(runIds, 100), (ids) => database
|
|
144
207
|
.select(attemptSelection)
|
|
145
208
|
.from(attempts)
|
|
@@ -153,11 +216,7 @@ export const queriesFor = (database) => {
|
|
|
153
216
|
const itemRunSummaries = Effect.fn("RunStoreQueries.itemRunSummaries")(function* (itemKeys) {
|
|
154
217
|
if (itemKeys.length === 0)
|
|
155
218
|
return [];
|
|
156
|
-
const storedRuns = (yield* Effect.forEach(EffectArray.chunksOf([...new Set(itemKeys)], 100), (keys) =>
|
|
157
|
-
.select(runSelection)
|
|
158
|
-
.from(runs)
|
|
159
|
-
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
160
|
-
.innerJoin(steps, eq(steps.runId, runs.runId))
|
|
219
|
+
const storedRuns = (yield* Effect.forEach(EffectArray.chunksOf([...new Set(itemKeys)], 100), (keys) => selectRuns()
|
|
161
220
|
.where(inArray(triggerEvents.itemKey, keys))
|
|
162
221
|
.execute()
|
|
163
222
|
.pipe(Effect.flatMap(decodeRunRows))))
|
|
@@ -165,24 +224,15 @@ export const queriesFor = (database) => {
|
|
|
165
224
|
.sort(newestRunFirst);
|
|
166
225
|
return yield* summariesWithAttempts(storedRuns);
|
|
167
226
|
});
|
|
168
|
-
// Every open Run plus the newest `closedLimit`
|
|
169
|
-
// Waiting run never falls out of the operator's view while history stays bounded.
|
|
227
|
+
// Every open Run plus the newest `closedLimit` closed ones, so an old waiting Run stays listed.
|
|
170
228
|
const runSummaries = Effect.fn("RunStoreQueries.runSummaries")(function* (closedLimit) {
|
|
171
229
|
const [openRuns, closedRuns] = yield* Effect.all([
|
|
172
|
-
|
|
173
|
-
.select(runSelection)
|
|
174
|
-
.from(runs)
|
|
175
|
-
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
176
|
-
.innerJoin(steps, eq(steps.runId, runs.runId))
|
|
230
|
+
selectRuns()
|
|
177
231
|
.where(openState)
|
|
178
232
|
.orderBy(desc(runs.openedAt), desc(runs.runId))
|
|
179
233
|
.execute()
|
|
180
234
|
.pipe(Effect.flatMap(decodeRunRows)),
|
|
181
|
-
|
|
182
|
-
.select(runSelection)
|
|
183
|
-
.from(runs)
|
|
184
|
-
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
185
|
-
.innerJoin(steps, eq(steps.runId, runs.runId))
|
|
235
|
+
selectRuns()
|
|
186
236
|
.where(notInArray(storedStateTag, [...RunState.OPEN_STATE_TAGS]))
|
|
187
237
|
.orderBy(desc(runs.openedAt), desc(runs.runId))
|
|
188
238
|
.limit(closedLimit)
|
|
@@ -192,6 +242,32 @@ export const queriesFor = (database) => {
|
|
|
192
242
|
const storedRuns = [...openRuns, ...closedRuns].sort(newestRunFirst);
|
|
193
243
|
return yield* summariesWithAttempts(storedRuns);
|
|
194
244
|
});
|
|
245
|
+
const runEvents = Effect.fn("RunStoreQueries.runEvents")(function* (runIds) {
|
|
246
|
+
// Chunks keep each read below the driver's bind-parameter limit.
|
|
247
|
+
const rows = yield* Effect.forEach(EffectArray.chunksOf([...new Set(runIds)], 100), (ids) => database
|
|
248
|
+
.select({
|
|
249
|
+
runId: runs.runId,
|
|
250
|
+
eventId: events.eventId,
|
|
251
|
+
dedupKey: events.dedupKey,
|
|
252
|
+
payload: events.payloadJson,
|
|
253
|
+
receivedAt: events.receivedAt,
|
|
254
|
+
})
|
|
255
|
+
.from(runs)
|
|
256
|
+
.innerJoin(events, eq(events.eventId, runs.eventId))
|
|
257
|
+
.where(inArray(runs.runId, ids))
|
|
258
|
+
.execute()
|
|
259
|
+
.pipe(Effect.flatMap(decodeRunEventRows)));
|
|
260
|
+
return rows.flat().map(runEventFromRow);
|
|
261
|
+
});
|
|
262
|
+
const closedRunSummaries = Effect.fn("RunStoreQueries.closedRunSummaries")(function* (limit) {
|
|
263
|
+
const closedRuns = yield* selectRuns()
|
|
264
|
+
.where(notInArray(storedStateTag, [...RunState.OPEN_STATE_TAGS]))
|
|
265
|
+
.orderBy(desc(runs.closedAt), desc(runs.runId))
|
|
266
|
+
.limit(limit)
|
|
267
|
+
.execute()
|
|
268
|
+
.pipe(Effect.flatMap(decodeRunRows));
|
|
269
|
+
return yield* summariesWithAttempts(closedRuns);
|
|
270
|
+
});
|
|
195
271
|
const revision = Effect.fn("RunStoreQueries.projectionRevision")(function* () {
|
|
196
272
|
const rows = yield* decodeProjectionRevision(yield* database
|
|
197
273
|
.select({ revision: projectionRevision.revision })
|
|
@@ -201,10 +277,12 @@ export const queriesFor = (database) => {
|
|
|
201
277
|
.execute());
|
|
202
278
|
return Option.fromNullishOr(rows[0]?.revision);
|
|
203
279
|
});
|
|
204
|
-
|
|
280
|
+
// A closed state sets `closedAt` to `at` or now; an open state clears it.
|
|
281
|
+
const updateState = Effect.fn("RunStoreQueries.updateState")(function* (runId, state, at) {
|
|
282
|
+
const closedAt = RunState.isOpen(state) ? null : encodeDateTime(at ?? (yield* DateTime.now));
|
|
205
283
|
const updated = yield* database
|
|
206
284
|
.update(runs)
|
|
207
|
-
.set({ stateJson: encodeRunState(state) })
|
|
285
|
+
.set({ stateJson: encodeRunState(state), closedAt })
|
|
208
286
|
.where(eq(runs.runId, runId))
|
|
209
287
|
.returning({ runId: runs.runId })
|
|
210
288
|
.execute();
|
|
@@ -212,6 +290,14 @@ export const queriesFor = (database) => {
|
|
|
212
290
|
return yield* new RunNotFoundError({ runId });
|
|
213
291
|
}
|
|
214
292
|
});
|
|
293
|
+
const resolveFailure = Effect.fn("RunStoreQueries.resolveFailure")(function* (runId, attemptId) {
|
|
294
|
+
yield* Effect.annotateCurrentSpan({ runId, attemptId });
|
|
295
|
+
yield* database
|
|
296
|
+
.update(runs)
|
|
297
|
+
.set({ resolvedFailureAttemptId: attemptId })
|
|
298
|
+
.where(eq(runs.runId, runId))
|
|
299
|
+
.execute();
|
|
300
|
+
});
|
|
215
301
|
const addAttempt = Effect.fn("RunStoreQueries.addAttempt")(function* (attempt) {
|
|
216
302
|
yield* database
|
|
217
303
|
.insert(attempts)
|
|
@@ -222,19 +308,87 @@ export const queriesFor = (database) => {
|
|
|
222
308
|
workerName: attempt.worker,
|
|
223
309
|
attemptNumber: attempt.number,
|
|
224
310
|
brainJson: encodeBrainSpec(attempt.brain),
|
|
311
|
+
task: attempt.task,
|
|
312
|
+
agent: attempt.agent,
|
|
313
|
+
operatorIntervened: attempt.operatorIntervened,
|
|
225
314
|
parentModel: Option.getOrNull(attempt.parentModel),
|
|
226
315
|
startedAt: encodeDateTime(attempt.startedAt),
|
|
227
316
|
outcomeJson: null,
|
|
317
|
+
endedAt: null,
|
|
318
|
+
salvageJson: null,
|
|
319
|
+
startJson: Option.match(attempt.start, { onNone: () => null, onSome: encodeAttemptStart }),
|
|
320
|
+
})
|
|
321
|
+
.execute();
|
|
322
|
+
});
|
|
323
|
+
const recordReattach = Effect.fn("RunStoreQueries.recordReattach")(function* (attemptId, reattachedAt) {
|
|
324
|
+
yield* Effect.annotateCurrentSpan({ attemptId });
|
|
325
|
+
yield* database
|
|
326
|
+
.update(attempts)
|
|
327
|
+
.set({ reattachedAt: encodeDateTime(reattachedAt) })
|
|
328
|
+
.where(eq(attempts.attemptId, attemptId))
|
|
329
|
+
.execute();
|
|
330
|
+
});
|
|
331
|
+
/** Returns the operator retry for the Run's next Attempt, or `None`. */
|
|
332
|
+
const nextStart = Effect.fn("RunStoreQueries.nextStart")(function* (runId) {
|
|
333
|
+
const rows = yield* database
|
|
334
|
+
.select({ nextStart: runs.nextStartJson })
|
|
335
|
+
.from(runs)
|
|
336
|
+
.where(eq(runs.runId, runId))
|
|
337
|
+
.execute();
|
|
338
|
+
return Option.fromNullishOr((yield* decodeNextStarts(rows))[0]?.nextStart);
|
|
339
|
+
});
|
|
340
|
+
const setNextStart = Effect.fn("RunStoreQueries.setNextStart")(function* (runId, start) {
|
|
341
|
+
yield* database
|
|
342
|
+
.update(runs)
|
|
343
|
+
.set({
|
|
344
|
+
nextStartJson: Option.match(start, { onNone: () => null, onSome: encodeAttemptStart }),
|
|
228
345
|
})
|
|
346
|
+
.where(eq(runs.runId, runId))
|
|
347
|
+
.execute();
|
|
348
|
+
});
|
|
349
|
+
const recordSalvage = Effect.fn("RunStoreQueries.recordSalvage")(function* (attemptId, salvage) {
|
|
350
|
+
yield* Effect.annotateCurrentSpan({ attemptId, salvage: salvage._tag });
|
|
351
|
+
yield* database
|
|
352
|
+
.update(attempts)
|
|
353
|
+
.set({ salvageJson: encodeSalvage(salvage) })
|
|
354
|
+
.where(eq(attempts.attemptId, attemptId))
|
|
229
355
|
.execute();
|
|
230
356
|
});
|
|
231
|
-
const
|
|
357
|
+
const recordComments = Effect.fn("RunStoreQueries.recordComments")(function* (attemptId, comments) {
|
|
358
|
+
yield* Effect.annotateCurrentSpan({ attemptId, comments: comments.length });
|
|
232
359
|
yield* database
|
|
233
360
|
.update(attempts)
|
|
234
|
-
.set({
|
|
361
|
+
.set({ commentsJson: encodeComments(comments) })
|
|
362
|
+
.where(eq(attempts.attemptId, attemptId))
|
|
363
|
+
.execute();
|
|
364
|
+
});
|
|
365
|
+
const closeAttempt = Effect.fn("RunStoreQueries.closeAttempt")(function* (attemptId, outcome, endedAt) {
|
|
366
|
+
yield* database
|
|
367
|
+
.update(attempts)
|
|
368
|
+
.set({ outcomeJson: encodeOutcome(outcome), endedAt: encodeDateTime(endedAt) })
|
|
235
369
|
.where(and(eq(attempts.attemptId, attemptId), isNull(attempts.outcomeJson)))
|
|
236
370
|
.execute();
|
|
237
371
|
});
|
|
372
|
+
const amend = Effect.fn("RunStoreQueries.amend")(function* (runId, context) {
|
|
373
|
+
yield* Effect.annotateCurrentSpan({ runId, agent: context.agent.name });
|
|
374
|
+
yield* database
|
|
375
|
+
.update(runs)
|
|
376
|
+
.set({
|
|
377
|
+
task: context.task,
|
|
378
|
+
agentJson: storedAgent(context.agent),
|
|
379
|
+
parentModel: Option.getOrNull(context.parentModel),
|
|
380
|
+
})
|
|
381
|
+
.where(eq(runs.runId, runId))
|
|
382
|
+
.execute();
|
|
383
|
+
});
|
|
384
|
+
const markIntervened = Effect.fn("RunStoreQueries.markIntervened")(function* (attemptId) {
|
|
385
|
+
yield* Effect.annotateCurrentSpan({ attemptId });
|
|
386
|
+
yield* database
|
|
387
|
+
.update(attempts)
|
|
388
|
+
.set({ operatorIntervened: true })
|
|
389
|
+
.where(eq(attempts.attemptId, attemptId))
|
|
390
|
+
.execute();
|
|
391
|
+
});
|
|
238
392
|
const updateAttemptParentModel = Effect.fn("RunStoreQueries.updateAttemptParentModel")(function* (attemptId, parentModel) {
|
|
239
393
|
yield* database
|
|
240
394
|
.update(attempts)
|
|
@@ -252,7 +406,7 @@ export const queriesFor = (database) => {
|
|
|
252
406
|
const approvalExists = Effect.fn("RunStoreQueries.approvalExists")(function* (runId) {
|
|
253
407
|
return (yield* database.$count(approvals, eq(approvals.runId, runId)).execute()) > 0;
|
|
254
408
|
});
|
|
255
|
-
const recordApproval = Effect.fn("RunStoreQueries.recordApproval")(function* (runId, approval) {
|
|
409
|
+
const recordApproval = Effect.fn("RunStoreQueries.recordApproval")(function* (runId, approval, source) {
|
|
256
410
|
yield* database
|
|
257
411
|
.insert(events)
|
|
258
412
|
.values({
|
|
@@ -263,30 +417,15 @@ export const queriesFor = (database) => {
|
|
|
263
417
|
})
|
|
264
418
|
.onConflictDoNothing()
|
|
265
419
|
.execute();
|
|
266
|
-
yield* database.insert(approvals).values({ runId, eventId: approval.id }).execute();
|
|
267
|
-
});
|
|
268
|
-
const requestWorkflowCommand = Effect.fn("RunStoreQueries.requestWorkflowCommand")(function* (runId, command) {
|
|
269
420
|
yield* database
|
|
270
|
-
.insert(
|
|
271
|
-
.values({
|
|
272
|
-
|
|
273
|
-
.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
.delete(workflowCommands)
|
|
278
|
-
.where(and(eq(workflowCommands.runId, runId), inArray(workflowCommands.command, ["Approve", "Resume"])))
|
|
279
|
-
.execute();
|
|
280
|
-
yield* requestWorkflowCommand(runId, "Interrupt");
|
|
281
|
-
});
|
|
282
|
-
const workflowCommandsFor = Effect.fn("RunStoreQueries.workflowCommandsFor")(function* (runId) {
|
|
283
|
-
yield* Effect.annotateCurrentSpan({ runId });
|
|
284
|
-
const rows = yield* database
|
|
285
|
-
.select({ runId: workflowCommands.runId, command: workflowCommands.command })
|
|
286
|
-
.from(workflowCommands)
|
|
287
|
-
.where(eq(workflowCommands.runId, runId))
|
|
421
|
+
.insert(approvals)
|
|
422
|
+
.values({
|
|
423
|
+
runId,
|
|
424
|
+
eventId: approval.id,
|
|
425
|
+
channel: source.channel,
|
|
426
|
+
approver: Option.getOrNull(source.approver),
|
|
427
|
+
})
|
|
288
428
|
.execute();
|
|
289
|
-
return (yield* decodeWorkflowCommands(rows)).map(({ command }) => command);
|
|
290
429
|
});
|
|
291
430
|
const createRun = Effect.fn("RunStoreQueries.createRun")(function* (submission, eventId, runId) {
|
|
292
431
|
const candidate = alias(triggerEvents, "candidate");
|
|
@@ -351,7 +490,48 @@ export const queriesFor = (database) => {
|
|
|
351
490
|
.limit(1)
|
|
352
491
|
.execute());
|
|
353
492
|
});
|
|
354
|
-
|
|
493
|
+
/** Returns the newest reopening of `eventId` (#267), with its reopening number. */
|
|
494
|
+
const newestReopening = Effect.fn("RunStoreQueries.newestReopening")(function* (eventId) {
|
|
495
|
+
const rows = yield* decodeReopenings(yield* database
|
|
496
|
+
.select({ ...eventRouteSelection, reopening: triggerEvents.reopening })
|
|
497
|
+
.from(events)
|
|
498
|
+
.innerJoin(triggerEvents, eq(triggerEvents.eventId, events.eventId))
|
|
499
|
+
.where(eq(triggerEvents.reopensEventId, eventId))
|
|
500
|
+
.orderBy(desc(triggerEvents.reopening))
|
|
501
|
+
.limit(1)
|
|
502
|
+
.execute());
|
|
503
|
+
return Option.map(Option.fromNullishOr(rows[0]), (row) => ({
|
|
504
|
+
submission: submissionFromRow(row),
|
|
505
|
+
reopening: row.reopening,
|
|
506
|
+
}));
|
|
507
|
+
});
|
|
508
|
+
/** Returns the trigger Event that `eventId` reopens or runs again, or `None` for a trigger's own Event. */
|
|
509
|
+
const reopenedEvent = Effect.fn("RunStoreQueries.reopenedEvent")(function* (eventId) {
|
|
510
|
+
const reopened = alias(events, "reopened_event");
|
|
511
|
+
const rows = yield* decodeReopenedEvents(yield* database
|
|
512
|
+
.select({ eventId: reopened.eventId, dedupKey: reopened.dedupKey })
|
|
513
|
+
.from(triggerEvents)
|
|
514
|
+
.innerJoin(reopened, eq(reopened.eventId, triggerEvents.reopensEventId))
|
|
515
|
+
.where(eq(triggerEvents.eventId, eventId))
|
|
516
|
+
.execute());
|
|
517
|
+
return Option.fromNullishOr(rows[0]);
|
|
518
|
+
});
|
|
519
|
+
/** Records that `eventId` came from running a closed Run again, and the requesting login. */
|
|
520
|
+
const recordRunAgain = Effect.fn("RunStoreQueries.recordRunAgain")(function* (eventId, again) {
|
|
521
|
+
yield* database
|
|
522
|
+
.insert(runAgainEvents)
|
|
523
|
+
.values({
|
|
524
|
+
eventId,
|
|
525
|
+
fromRunId: again.from,
|
|
526
|
+
requestedBy: Option.getOrNull(again.requestedBy),
|
|
527
|
+
})
|
|
528
|
+
.execute();
|
|
529
|
+
});
|
|
530
|
+
/**
|
|
531
|
+
* Records a trigger Event and its route once, and returns the route as first recorded.
|
|
532
|
+
* `reopens` is the earlier Event that this one reopens.
|
|
533
|
+
*/
|
|
534
|
+
const recordTriggerEvent = Effect.fn("RunStoreQueries.recordTriggerEvent")(function* (submission, reopens = Option.none()) {
|
|
355
535
|
yield* database
|
|
356
536
|
.insert(events)
|
|
357
537
|
.values({
|
|
@@ -373,6 +553,7 @@ export const queriesFor = (database) => {
|
|
|
373
553
|
loopName: submission.loop,
|
|
374
554
|
stepName: submission.step,
|
|
375
555
|
approval: submission.approval,
|
|
556
|
+
permissions: submission.permissions,
|
|
376
557
|
cycleLimit: Option.getOrNull(submission.cycleLimit),
|
|
377
558
|
cycleGeneration: submission.cycleGeneration,
|
|
378
559
|
repository: submission.repository,
|
|
@@ -380,6 +561,11 @@ export const queriesFor = (database) => {
|
|
|
380
561
|
task: submission.task,
|
|
381
562
|
agentJson: storedAgent(submission.agent),
|
|
382
563
|
parentModel: Option.getOrNull(submission.parentModel),
|
|
564
|
+
reopensEventId: Option.getOrNull(Option.map(reopens, ({ eventId }) => eventId)),
|
|
565
|
+
reopening: Option.match(reopens, {
|
|
566
|
+
onNone: () => 0,
|
|
567
|
+
onSome: ({ reopening }) => reopening,
|
|
568
|
+
}),
|
|
383
569
|
})
|
|
384
570
|
.execute();
|
|
385
571
|
return submission;
|
|
@@ -391,6 +577,16 @@ export const queriesFor = (database) => {
|
|
|
391
577
|
const current = routes[0];
|
|
392
578
|
if (current === undefined)
|
|
393
579
|
return Option.none();
|
|
580
|
+
const suppression = yield* loopItemQueries(database).suppression(current.loop, current.itemKey);
|
|
581
|
+
if (Option.isSome(suppression)) {
|
|
582
|
+
yield* Effect.logInfo("Queued Events wait while a suppression keeps the Item", {
|
|
583
|
+
runId,
|
|
584
|
+
suppressedBy: suppression.value.runId,
|
|
585
|
+
loop: current.loop,
|
|
586
|
+
itemKey: current.itemKey,
|
|
587
|
+
});
|
|
588
|
+
return Option.none();
|
|
589
|
+
}
|
|
394
590
|
const queuedRows = yield* database
|
|
395
591
|
.select(eventRouteSelection)
|
|
396
592
|
.from(events)
|
|
@@ -399,103 +595,53 @@ export const queriesFor = (database) => {
|
|
|
399
595
|
.leftJoin(ignoredEvents, eq(ignoredEvents.eventId, events.eventId))
|
|
400
596
|
.where(and(eq(triggerEvents.loopName, current.loop), eq(triggerEvents.itemKey, current.itemKey), cycleQueries(database).currentEvent, isNull(runs.runId), isNull(ignoredEvents.eventId)))
|
|
401
597
|
.orderBy(desc(events.receivedAt))
|
|
402
|
-
.limit(1)
|
|
403
598
|
.execute();
|
|
404
599
|
const queued = yield* decodeEventRoutes(queuedRows);
|
|
405
|
-
return Option.fromNullishOr(queued[0]).pipe(Option.map(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
.insert(attemptTranscripts)
|
|
410
|
-
.values({
|
|
411
|
-
attemptId: envelope.attemptId,
|
|
412
|
-
direction,
|
|
413
|
-
channelSequence: envelope.sequence,
|
|
414
|
-
envelope: encodeEnvelope(envelope),
|
|
415
|
-
receivedAt: encodeDateTime(receivedAt),
|
|
416
|
-
})
|
|
417
|
-
.onConflictDoNothing()
|
|
418
|
-
.execute();
|
|
600
|
+
return Option.fromNullishOr(queued[0]).pipe(Option.map((newest) => ({
|
|
601
|
+
submission: submissionFromRow(newest),
|
|
602
|
+
eventIds: queued.map((row) => row.eventId),
|
|
603
|
+
})));
|
|
419
604
|
});
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
attemptId: query.attemptId,
|
|
423
|
-
query: query._tag,
|
|
424
|
-
});
|
|
425
|
-
const cursor = Match.valueTags(query, {
|
|
426
|
-
TranscriptAllQuery: ({ attemptId }) => eq(attemptTranscripts.attemptId, attemptId),
|
|
427
|
-
TranscriptTailQuery: ({ attemptId, after }) => and(eq(attemptTranscripts.attemptId, attemptId), gt(attemptTranscripts.sequence, after)),
|
|
428
|
-
TranscriptPageQuery: ({ attemptId, before }) => Option.match(before, {
|
|
429
|
-
onNone: () => eq(attemptTranscripts.attemptId, attemptId),
|
|
430
|
-
onSome: (sequence) => and(eq(attemptTranscripts.attemptId, attemptId), lt(attemptTranscripts.sequence, sequence)),
|
|
431
|
-
}),
|
|
432
|
-
});
|
|
433
|
-
const selection = database
|
|
434
|
-
.select({
|
|
435
|
-
sequence: attemptTranscripts.sequence,
|
|
436
|
-
attemptId: attemptTranscripts.attemptId,
|
|
437
|
-
direction: attemptTranscripts.direction,
|
|
438
|
-
envelope: attemptTranscripts.envelope,
|
|
439
|
-
receivedAt: attemptTranscripts.receivedAt,
|
|
440
|
-
})
|
|
441
|
-
.from(attemptTranscripts)
|
|
442
|
-
.where(cursor);
|
|
443
|
-
// A limited read walks the tail index backwards for the newest N rows and
|
|
444
|
-
// re-sorts ascending, so callers always receive append order.
|
|
445
|
-
const rows = yield* Match.valueTags(query, {
|
|
446
|
-
TranscriptAllQuery: () => selection.orderBy(asc(attemptTranscripts.sequence)).execute(),
|
|
447
|
-
TranscriptTailQuery: ({ limit }) => selection.orderBy(asc(attemptTranscripts.sequence)).limit(limit).execute(),
|
|
448
|
-
TranscriptPageQuery: ({ limit }) => selection.orderBy(desc(attemptTranscripts.sequence)).limit(limit).execute(),
|
|
449
|
-
});
|
|
450
|
-
// Transcript rows are usine-owned, so an unknown key is corruption.
|
|
451
|
-
return (yield* decodeTranscriptRows(rows, { onExcessProperty: "error" }))
|
|
452
|
-
.map(transcriptEntryFromRow)
|
|
453
|
-
.sort((left, right) => left.sequence - right.sequence);
|
|
454
|
-
});
|
|
455
|
-
const recoverable = Effect.fn("RunStoreQueries.recoverable")(function* () {
|
|
605
|
+
const runIdsToDrive = Effect.fn("RunStoreQueries.runIdsToDrive")(function* () {
|
|
606
|
+
const waiting = "Waiting";
|
|
456
607
|
const rows = yield* database
|
|
457
|
-
.select({
|
|
458
|
-
...eventRouteSelection,
|
|
459
|
-
runId: runs.runId,
|
|
460
|
-
state: runs.stateJson,
|
|
461
|
-
})
|
|
608
|
+
.select({ runId: runs.runId })
|
|
462
609
|
.from(runs)
|
|
463
|
-
.
|
|
464
|
-
.innerJoin(triggerEvents, eq(triggerEvents.eventId, events.eventId))
|
|
465
|
-
.where(openState)
|
|
610
|
+
.where(and(openState, ne(storedStateTag, waiting)))
|
|
466
611
|
.execute();
|
|
467
|
-
return yield*
|
|
612
|
+
return (yield* decodeRunIds(rows)).map(({ runId }) => runId);
|
|
468
613
|
});
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
? -1
|
|
481
|
-
: 1);
|
|
482
|
-
});
|
|
483
|
-
const completeWorkflowCommand = Effect.fn("RunStoreQueries.completeWorkflowCommand")(function* (runId, command) {
|
|
484
|
-
yield* database
|
|
485
|
-
.delete(workflowCommands)
|
|
486
|
-
.where(and(eq(workflowCommands.runId, runId), eq(workflowCommands.command, command)))
|
|
614
|
+
// The newest closed Run of each Item that has a current queued Event and no open Run, excluding
|
|
615
|
+
// Items waiting for a cycle-budget reset or under a suppression.
|
|
616
|
+
const pendingContinuations = Effect.fn("RunStoreQueries.pendingContinuations")(function* () {
|
|
617
|
+
const items = yield* database
|
|
618
|
+
.selectDistinct({ loop: triggerEvents.loopName, itemKey: triggerEvents.itemKey })
|
|
619
|
+
.from(triggerEvents)
|
|
620
|
+
.leftJoin(runs, eq(runs.eventId, triggerEvents.eventId))
|
|
621
|
+
.leftJoin(ignoredEvents, eq(ignoredEvents.eventId, triggerEvents.eventId))
|
|
622
|
+
.leftJoin(itemCycleBudgets, eq(itemCycleBudgets.itemKey, triggerEvents.itemKey))
|
|
623
|
+
.leftJoin(loopItems, and(eq(loopItems.loopName, triggerEvents.loopName), eq(loopItems.itemKey, triggerEvents.itemKey)))
|
|
624
|
+
.where(and(isNull(runs.runId), isNull(ignoredEvents.eventId), isNull(itemCycleBudgets.askJson), isNull(loopItems.suppressedByRunId), cycleQueries(database).currentEvent))
|
|
487
625
|
.execute();
|
|
626
|
+
const decoded = yield* Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ loop: LoopName, itemKey: ItemKey })))(items);
|
|
627
|
+
const closed = yield* Effect.forEach(decoded, ({ loop, itemKey }) => Effect.gen(function* () {
|
|
628
|
+
if ((yield* blockingRun(loop, itemKey)).length > 0)
|
|
629
|
+
return [];
|
|
630
|
+
return yield* decodeRunIds(yield* database
|
|
631
|
+
.select({ runId: runs.runId })
|
|
632
|
+
.from(runs)
|
|
633
|
+
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
634
|
+
.where(and(eq(triggerEvents.loopName, loop), eq(triggerEvents.itemKey, itemKey)))
|
|
635
|
+
.orderBy(desc(runs.openedAt), desc(runs.runId))
|
|
636
|
+
.limit(1)
|
|
637
|
+
.execute());
|
|
638
|
+
}));
|
|
639
|
+
return closed.flat().map(({ runId }) => runId);
|
|
488
640
|
});
|
|
489
|
-
const
|
|
490
|
-
const rows = yield* decodeRunIds(yield* database
|
|
491
|
-
.select({ runId: runs.runId })
|
|
492
|
-
.from(runs)
|
|
493
|
-
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
494
|
-
.where(and(eq(triggerEvents.loopName, loop), eq(triggerEvents.itemKey, itemKey), unstartedState))
|
|
495
|
-
.execute());
|
|
641
|
+
const revokeQueued = Effect.fn("RunStoreQueries.revokeQueued")(function* (loop, itemKey, eventIds) {
|
|
496
642
|
const candidate = alias(triggerEvents, "candidate");
|
|
497
643
|
const candidateRun = alias(runs, "candidate_run");
|
|
498
|
-
yield* database
|
|
644
|
+
const ignored = yield* database
|
|
499
645
|
.insert(ignoredEvents)
|
|
500
646
|
.select(database
|
|
501
647
|
.select({
|
|
@@ -504,40 +650,67 @@ export const queriesFor = (database) => {
|
|
|
504
650
|
})
|
|
505
651
|
.from(candidate)
|
|
506
652
|
.leftJoin(candidateRun, eq(candidateRun.eventId, candidate.eventId))
|
|
507
|
-
.where(and(eq(candidate.loopName, loop), eq(candidate.itemKey, itemKey), isNull(candidateRun.runId))))
|
|
653
|
+
.where(and(eq(candidate.loopName, loop), eq(candidate.itemKey, itemKey), inArray(candidate.eventId, eventIds), isNull(candidateRun.runId))))
|
|
508
654
|
.onConflictDoNothing()
|
|
655
|
+
.returning({ eventId: ignoredEvents.eventId })
|
|
509
656
|
.execute();
|
|
657
|
+
return ignored.length;
|
|
658
|
+
});
|
|
659
|
+
const revokeUnstarted = Effect.fn("RunStoreQueries.revokeUnstarted")(function* (loop, itemKey) {
|
|
660
|
+
const rows = yield* decodeRunIds(yield* database
|
|
661
|
+
.select({ runId: runs.runId })
|
|
662
|
+
.from(runs)
|
|
663
|
+
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
664
|
+
.where(and(eq(triggerEvents.loopName, loop), eq(triggerEvents.itemKey, itemKey), unstartedState))
|
|
665
|
+
.execute());
|
|
666
|
+
const queued = database
|
|
667
|
+
.select({ eventId: triggerEvents.eventId })
|
|
668
|
+
.from(triggerEvents)
|
|
669
|
+
.where(and(eq(triggerEvents.loopName, loop), eq(triggerEvents.itemKey, itemKey)));
|
|
670
|
+
yield* revokeQueued(loop, itemKey, queued);
|
|
510
671
|
return rows;
|
|
511
672
|
});
|
|
512
673
|
return {
|
|
674
|
+
salvageCandidates,
|
|
675
|
+
salvageCandidate,
|
|
513
676
|
cycles: cycleQueries(database),
|
|
677
|
+
loopItems: loopItemQueries(database),
|
|
678
|
+
...transcriptQueries(database),
|
|
514
679
|
addAttempt,
|
|
515
|
-
|
|
680
|
+
recordReattach,
|
|
681
|
+
nextStart,
|
|
682
|
+
setNextStart,
|
|
683
|
+
recordSalvage,
|
|
684
|
+
recordComments,
|
|
685
|
+
amend,
|
|
686
|
+
markIntervened,
|
|
516
687
|
approvalExists,
|
|
517
688
|
blockingRun,
|
|
518
689
|
closeAttempt,
|
|
519
|
-
completeWorkflowCommand,
|
|
520
690
|
createRun,
|
|
521
691
|
eventRouteByRun,
|
|
522
|
-
workflowCommandsFor,
|
|
523
692
|
ignoredEvent,
|
|
693
|
+
newestReopening,
|
|
524
694
|
nextQueued,
|
|
525
|
-
|
|
695
|
+
reopenedEvent,
|
|
696
|
+
recordRunAgain,
|
|
697
|
+
pendingContinuations,
|
|
526
698
|
recordTriggerEvent,
|
|
527
|
-
|
|
699
|
+
runIdsToDrive,
|
|
528
700
|
recordApproval,
|
|
529
|
-
requestInterrupt,
|
|
530
|
-
requestWorkflowCommand,
|
|
531
701
|
revision,
|
|
702
|
+
revokeQueued,
|
|
532
703
|
revokeUnstarted,
|
|
533
704
|
runSummaries,
|
|
705
|
+
closedRunSummaries,
|
|
534
706
|
itemRunSummaries,
|
|
707
|
+
runEvents,
|
|
535
708
|
runForEvent,
|
|
536
709
|
snapshot,
|
|
537
|
-
transcript,
|
|
538
710
|
updateStepResult,
|
|
539
711
|
updateAttemptParentModel,
|
|
540
712
|
updateState,
|
|
713
|
+
resolveFailure,
|
|
541
714
|
};
|
|
542
715
|
};
|
|
543
716
|
//# sourceMappingURL=queries.js.map
|