@vercel/factory 0.0.9 → 0.0.10
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/dist/{approval-MoiwGpkh.d.mts → approval-CDiBco2V.d.mts} +14 -6
- package/dist/{task-DMHuvBI0.mjs → change-BPYhWKuL.mjs} +119 -99
- package/dist/change-BPYhWKuL.mjs.map +1 -0
- package/dist/eve/index.d.mts +81 -11
- package/dist/eve/index.mjs +83 -3
- package/dist/eve/index.mjs.map +1 -1
- package/dist/{github-DMOypeIu.d.mts → github-wThBfbm-.d.mts} +99 -2
- package/dist/github.d.mts +2 -2
- package/dist/github.mjs +195 -1
- package/dist/github.mjs.map +1 -1
- package/dist/index.d.mts +71 -2
- package/dist/index.mjs +201 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/task-DMHuvBI0.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as StoredEntry, i as StoreDriver, n as RecordExistsError, o as StoredRecord, r as RecordNotFoundError, s as VersionConflictError, t as CollectionName } from "./driver-CrZsy7sm.mjs";
|
|
2
2
|
import { a as factoryConfigSchema, i as factoryConfigRepositorySchema, n as FactoryConfigInput, r as defineFactory, t as FactoryConfig } from "./factory-config-Bu4mklm9.mjs";
|
|
3
3
|
import { a as signalSchema, c as ReplyTo, d as replyToSchema, f as schemaVersionSchema, i as signalOutcomeSchema, l as SCHEMA_VERSION, m as vocabularySchema, n as SignalOutcome, o as signalSourceSchema, p as usdSchema, r as SignalSource, s as standardSignalSources, t as Signal, u as isoDateTimeSchema } from "./signal-CHVEmzvI.mjs";
|
|
4
|
-
import { $ as
|
|
4
|
+
import { $ as Cost, A as FactoryStores, At as repositoryIdsSchema, B as standardTaskKinds, C as ChangePatch, Ct as RepositoryId, D as CreateStoresOptions, Dt as factoryIdSchema, E as CreateSignalInput, Et as changeIdSchema, F as TaskApproval, G as taskOriginSchema, H as taskDevboxSchema, I as TaskKind, J as taskStateSchema, K as taskQuestionSchema, L as TaskState, M as TaskTransitionOptions, Mt as taskIdSchema, N as createStores, O as CreateTaskInput, Ot as receiptIdSchema, P as Task, Q as repositorySlugSchema, R as coordinationRepositoryIdsSchema, S as ChangeAssociationError, St as ReceiptId, T as CreateChangeInput, Tt as TaskId, U as taskExecutionRepositoryIdsSchema, V as taskApprovalSchema, W as taskKindSchema, X as RepositorySlug, Y as Repository, Z as repositorySchema, _ as findingVerificationMethodSchema, _t as changeSchema, a as approvalRuleSchema, at as receiptKindSchema, b as ApprovalDecision, bt as ChangeId, c as Finding, ct as receiptStateSchema, d as FindingVerification, dt as standardReceiptStates, et as Receipt, f as findingCategorySchema, ft as Change, g as findingVerificationLevelSchema, gt as changeEvidenceSchema, h as findingSchema, ht as Level, i as approvalPolicySchema, it as costSchema, j as TaskPatch, jt as signalIdSchema, k as DecideSignalOptions, kt as repositoryIdSchema, l as FindingCategory, lt as standardReceiptKinds, m as findingPrioritySchema, mt as ChangeState, n as ApprovalRule, nt as ReceiptPhase, o as defaultApprovalPolicy, ot as receiptPhaseSchema, p as findingFingerprint, pt as ChangeEvidence, q as taskSchema, r as ApprovalSubject, rt as ReceiptState, s as requiresApproval, st as receiptSchema, t as ApprovalPolicy, tt as ReceiptKind, u as FindingPriority, ut as standardReceiptPhases, v as findingVerificationSchema, vt as changeStateSchema, w as ChangeTransitionOptions, wt as SignalId, x as ApprovalError, xt as FactoryId, y as AppendReceiptInput, yt as levelSchema, z as harnessSchema } from "./approval-CDiBco2V.mjs";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
//#region src/briefs.d.ts
|
|
7
7
|
/** The finding behind a task, when its signal carries one. */
|
|
@@ -50,6 +50,75 @@ declare const DEFAULT_MAX_LAUNCH_ATTEMPTS = 45;
|
|
|
50
50
|
*/
|
|
51
51
|
declare function dispatchTask(stores: FactoryStores, taskId: TaskId, options: DispatchOptions): Promise<Task>;
|
|
52
52
|
//#endregion
|
|
53
|
+
//#region src/metrics.d.ts
|
|
54
|
+
/**
|
|
55
|
+
* The derived review bucket from the default risk x confidence table. Derived
|
|
56
|
+
* at read time, never stored; "blocked" is a bucket, not a change state.
|
|
57
|
+
*/
|
|
58
|
+
type ReviewBucket = "stamp" | "review" | "blocked";
|
|
59
|
+
declare function reviewBucket(risk: Level, confidence: Level): ReviewBucket;
|
|
60
|
+
interface LedgerRows {
|
|
61
|
+
changes: readonly Change[];
|
|
62
|
+
signals: readonly Signal[];
|
|
63
|
+
receipts: readonly Receipt[];
|
|
64
|
+
tasks: readonly Task[];
|
|
65
|
+
}
|
|
66
|
+
interface MetricsOptions {
|
|
67
|
+
/** Evaluation time; defaults to the current time. */
|
|
68
|
+
now?: Date;
|
|
69
|
+
/** Days a landed change must hold without a rollback. Default 7. */
|
|
70
|
+
holdDays?: number;
|
|
71
|
+
}
|
|
72
|
+
interface LedgerMetrics {
|
|
73
|
+
/**
|
|
74
|
+
* Changes a human merged that reached deployed (rolled_out for high risk)
|
|
75
|
+
* and did not roll back within the hold window. Includes provisional ones.
|
|
76
|
+
*/
|
|
77
|
+
landed: number;
|
|
78
|
+
/** Landed changes still inside the hold window; the number may drop. */
|
|
79
|
+
provisional: number;
|
|
80
|
+
/** landed / (landed + rolled back + closed after ready). Null when nothing decided. */
|
|
81
|
+
stickRate: number | null;
|
|
82
|
+
/**
|
|
83
|
+
* Not computable from ledger rows: needs a record of human commits pushed
|
|
84
|
+
* to the pull request after ready, which nothing writes today.
|
|
85
|
+
*/
|
|
86
|
+
stampedClean: null;
|
|
87
|
+
/** Median ms from the ready receipt to the merged receipt, per review bucket. */
|
|
88
|
+
humanTimeMs: {
|
|
89
|
+
stamp: number | null;
|
|
90
|
+
review: number | null;
|
|
91
|
+
};
|
|
92
|
+
/** Sum of settled task cost over landed changes / landed. Null until budgets settle. */
|
|
93
|
+
costPerLandedUsd: number | null;
|
|
94
|
+
/** Signals triage turned away plus changes closed in verification. */
|
|
95
|
+
filtered: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The six product metrics from ledger rows alone. One rule governs them: a
|
|
99
|
+
* change counts only after a human merged it and it stayed. Activity that
|
|
100
|
+
* never landed is not a result. Compute over rows already filtered to the
|
|
101
|
+
* period and repositories you are reporting on.
|
|
102
|
+
*/
|
|
103
|
+
declare function computeLedgerMetrics(rows: LedgerRows, options?: MetricsOptions): LedgerMetrics;
|
|
104
|
+
type HealthColor = "green" | "yellow" | "red";
|
|
105
|
+
interface HealthOptions {
|
|
106
|
+
now?: Date;
|
|
107
|
+
/** Open-change cap from factory configuration; omitted disables that check. */
|
|
108
|
+
maxOpenChanges?: number;
|
|
109
|
+
}
|
|
110
|
+
interface RepositoryHealth {
|
|
111
|
+
color: HealthColor;
|
|
112
|
+
/** Every rule that fired, so the color is explainable from rows. */
|
|
113
|
+
reasons: string[];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Green, yellow, or red from rows, never from a model. Budget and onboarding
|
|
117
|
+
* signals are not evaluated yet: budgets do not settle and onboarding
|
|
118
|
+
* investigations are not recorded, so those spec rules have no rows to read.
|
|
119
|
+
*/
|
|
120
|
+
declare function computeRepositoryHealth(rows: LedgerRows, options?: HealthOptions): RepositoryHealth;
|
|
121
|
+
//#endregion
|
|
53
122
|
//#region src/schema/budget.d.ts
|
|
54
123
|
declare const budgetMonthSchema: z.ZodString;
|
|
55
124
|
declare const budgetSchema: z.ZodObject<{
|
|
@@ -139,5 +208,5 @@ interface SweepResult {
|
|
|
139
208
|
*/
|
|
140
209
|
declare function sweepQueued(stores: FactoryStores, options: SweepOptions): Promise<SweepResult>;
|
|
141
210
|
//#endregion
|
|
142
|
-
export { AdoptPullRequestInput, AppendReceiptInput, ApprovalDecision, ApprovalError, ApprovalPolicy, ApprovalRule, ApprovalSubject, Budget, Change, ChangeEvidence, ChangeId, ChangePatch, ChangeState, ChangeTransitionOptions, CollectionName, Cost, CreateChangeInput, CreateSignalInput, CreateStoresOptions, CreateTaskInput, DEFAULT_MAX_LAUNCH_ATTEMPTS, DecideSignalOptions, DispatchOptions, DispatchRefusedError, FactoryConfig, FactoryConfigInput, FactoryId, FactoryStores, Finding, FindingCategory, FindingPriority, FindingVerification, InvalidTransitionError, Level, Receipt, ReceiptId, ReceiptKind, ReceiptPhase, ReceiptState, RecordExistsError, RecordNotFoundError, ReplyTo, Repository, RepositoryId, RepositorySlug, SCHEMA_VERSION, Signal, SignalCreator, SignalId, SignalOutcome, SignalSource, StoreDriver, StoredEntry, StoredRecord, SweepOptions, SweepResult, Task, TaskApproval, TaskId, TaskKind, TaskLauncher, TaskPatch, TaskState, TaskTransitionOptions, Transition, TransitionActor, VersionConflictError, adoptPullRequest, approvalPolicySchema, approvalRuleSchema, assertTransition, budgetMonthSchema, budgetSchema, changeEvidenceSchema, changeIdSchema, changeInitialState, changeSchema, changeStateSchema, changeTransitions, costSchema, createInMemoryDriver, createSignalCreator, createStores, defaultApprovalPolicy, defineFactory, dispatchTask, factoryConfigRepositorySchema, factoryConfigSchema, factoryIdSchema, findingCategorySchema, findingFingerprint, findingForTask, findingPrioritySchema, findingSchema, findingVerificationLevelSchema, findingVerificationMethodSchema, findingVerificationSchema, fixBrief, harnessSchema, isTransitionAllowed, isoDateTimeSchema, legalNextStates, levelSchema, receiptIdSchema, receiptKindSchema, receiptPhaseSchema, receiptSchema, receiptStateSchema, replyToSchema, repositoryIdSchema, repositoryIdsSchema, repositorySchema, repositorySlugSchema, requiresApproval, scanBrief, schemaVersionSchema, signalIdSchema, signalInitialOutcome, signalOutcomeSchema, signalOutcomeTransitions, signalSchema, signalSourceSchema, standardReceiptKinds, standardReceiptPhases, standardReceiptStates, standardSignalSources, standardTaskKinds, sweepQueued, taskApprovalSchema, taskDevboxSchema, taskIdSchema, taskInitialState, taskKindSchema, taskOriginSchema, taskQuestionSchema, taskSchema, taskStateSchema, taskTransitions, terminalStates, usdSchema, vocabularySchema };
|
|
211
|
+
export { AdoptPullRequestInput, AppendReceiptInput, ApprovalDecision, ApprovalError, ApprovalPolicy, ApprovalRule, ApprovalSubject, Budget, Change, ChangeAssociationError, ChangeEvidence, ChangeId, ChangePatch, ChangeState, ChangeTransitionOptions, CollectionName, Cost, CreateChangeInput, CreateSignalInput, CreateStoresOptions, CreateTaskInput, DEFAULT_MAX_LAUNCH_ATTEMPTS, DecideSignalOptions, DispatchOptions, DispatchRefusedError, FactoryConfig, FactoryConfigInput, FactoryId, FactoryStores, Finding, FindingCategory, FindingPriority, FindingVerification, HealthColor, HealthOptions, InvalidTransitionError, LedgerMetrics, LedgerRows, Level, MetricsOptions, Receipt, ReceiptId, ReceiptKind, ReceiptPhase, ReceiptState, RecordExistsError, RecordNotFoundError, ReplyTo, Repository, RepositoryHealth, RepositoryId, RepositorySlug, ReviewBucket, SCHEMA_VERSION, Signal, SignalCreator, SignalId, SignalOutcome, SignalSource, StoreDriver, StoredEntry, StoredRecord, SweepOptions, SweepResult, Task, TaskApproval, TaskId, TaskKind, TaskLauncher, TaskPatch, TaskState, TaskTransitionOptions, Transition, TransitionActor, VersionConflictError, adoptPullRequest, approvalPolicySchema, approvalRuleSchema, assertTransition, budgetMonthSchema, budgetSchema, changeEvidenceSchema, changeIdSchema, changeInitialState, changeSchema, changeStateSchema, changeTransitions, computeLedgerMetrics, computeRepositoryHealth, coordinationRepositoryIdsSchema, costSchema, createInMemoryDriver, createSignalCreator, createStores, defaultApprovalPolicy, defineFactory, dispatchTask, factoryConfigRepositorySchema, factoryConfigSchema, factoryIdSchema, findingCategorySchema, findingFingerprint, findingForTask, findingPrioritySchema, findingSchema, findingVerificationLevelSchema, findingVerificationMethodSchema, findingVerificationSchema, fixBrief, harnessSchema, isTransitionAllowed, isoDateTimeSchema, legalNextStates, levelSchema, receiptIdSchema, receiptKindSchema, receiptPhaseSchema, receiptSchema, receiptStateSchema, replyToSchema, repositoryIdSchema, repositoryIdsSchema, repositorySchema, repositorySlugSchema, requiresApproval, reviewBucket, scanBrief, schemaVersionSchema, signalIdSchema, signalInitialOutcome, signalOutcomeSchema, signalOutcomeTransitions, signalSchema, signalSourceSchema, standardReceiptKinds, standardReceiptPhases, standardReceiptStates, standardSignalSources, standardTaskKinds, sweepQueued, taskApprovalSchema, taskDevboxSchema, taskExecutionRepositoryIdsSchema, taskIdSchema, taskInitialState, taskKindSchema, taskOriginSchema, taskQuestionSchema, taskSchema, taskStateSchema, taskTransitions, terminalStates, usdSchema, vocabularySchema };
|
|
143
212
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as repositoryIdsSchema, c as SCHEMA_VERSION, d as schemaVersionSchema, f as usdSchema, i as repositoryIdSchema, l as isoDateTimeSchema, n as factoryIdSchema, o as signalIdSchema, p as vocabularySchema, r as receiptIdSchema, s as taskIdSchema, t as changeIdSchema, u as replyToSchema } from "./id-DwCihQr4.mjs";
|
|
2
2
|
import { a as findingCategorySchema, c as findingSchema, d as findingVerificationSchema, i as requiresApproval, l as findingVerificationLevelSchema, n as approvalRuleSchema, o as findingFingerprint, r as defaultApprovalPolicy, s as findingPrioritySchema, t as approvalPolicySchema, u as findingVerificationMethodSchema } from "./approval-DdvEKLiE.mjs";
|
|
3
|
+
import { _ as taskSchema, a as DEFAULT_MAX_LAUNCH_ATTEMPTS, c as coordinationRepositoryIdsSchema, d as taskApprovalSchema, f as taskDevboxSchema, g as taskQuestionSchema, h as taskOriginSchema, i as levelSchema, l as harnessSchema, m as taskKindSchema, n as changeSchema, o as DispatchRefusedError, p as taskExecutionRepositoryIdsSchema, r as changeStateSchema, s as dispatchTask, t as changeEvidenceSchema, u as standardTaskKinds, v as taskStateSchema } from "./change-BPYhWKuL.mjs";
|
|
3
4
|
import { n as RecordNotFoundError, r as VersionConflictError, t as RecordExistsError } from "./driver-Bbd25rNM.mjs";
|
|
4
|
-
import { a as taskKindSchema, c as taskSchema, d as changeSchema, f as changeStateSchema, g as dispatchTask, h as DispatchRefusedError, i as taskDevboxSchema, l as taskStateSchema, m as DEFAULT_MAX_LAUNCH_ATTEMPTS, n as standardTaskKinds, o as taskOriginSchema, p as levelSchema, r as taskApprovalSchema, s as taskQuestionSchema, t as harnessSchema, u as changeEvidenceSchema } from "./task-DMHuvBI0.mjs";
|
|
5
5
|
import { a as repositorySlugSchema, i as repositorySchema, n as factoryConfigRepositorySchema, r as factoryConfigSchema, t as defineFactory } from "./factory-config-ifd3JxiU.mjs";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -56,6 +56,168 @@ async function adoptPullRequest(stores, input) {
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
//#endregion
|
|
59
|
+
//#region src/metrics.ts
|
|
60
|
+
const bucketTable = {
|
|
61
|
+
low: {
|
|
62
|
+
low: "review",
|
|
63
|
+
medium: "stamp",
|
|
64
|
+
high: "stamp"
|
|
65
|
+
},
|
|
66
|
+
medium: {
|
|
67
|
+
low: "review",
|
|
68
|
+
medium: "review",
|
|
69
|
+
high: "stamp"
|
|
70
|
+
},
|
|
71
|
+
high: {
|
|
72
|
+
low: "blocked",
|
|
73
|
+
medium: "review",
|
|
74
|
+
high: "review"
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
function reviewBucket(risk, confidence) {
|
|
78
|
+
return bucketTable[risk][confidence];
|
|
79
|
+
}
|
|
80
|
+
function collectFacts(rows) {
|
|
81
|
+
const byChange = /* @__PURE__ */ new Map();
|
|
82
|
+
for (const receipt of rows.receipts) {
|
|
83
|
+
if (receipt.kind !== "change" || receipt.changeId === void 0) continue;
|
|
84
|
+
const list = byChange.get(receipt.changeId) ?? [];
|
|
85
|
+
list.push(receipt);
|
|
86
|
+
byChange.set(receipt.changeId, list);
|
|
87
|
+
}
|
|
88
|
+
const at = (receipts, state) => {
|
|
89
|
+
const found = receipts?.find((receipt) => receipt.state === state);
|
|
90
|
+
return found === void 0 ? null : Date.parse(found.recordedAt);
|
|
91
|
+
};
|
|
92
|
+
return rows.changes.map((change) => {
|
|
93
|
+
const receipts = byChange.get(change.id);
|
|
94
|
+
const requiredState = change.risk === "high" ? "rolled_out" : "deployed";
|
|
95
|
+
return {
|
|
96
|
+
change,
|
|
97
|
+
readyAt: at(receipts, "ready"),
|
|
98
|
+
mergedAt: at(receipts, "merged"),
|
|
99
|
+
reachedAt: at(receipts, requiredState),
|
|
100
|
+
rolledBackAt: at(receipts, "rolled_back")
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/** True for a change a human saw as ready and then closed unmerged. */
|
|
105
|
+
function closedAfterReady(facts) {
|
|
106
|
+
return facts.change.state === "closed" && (facts.readyAt !== null || facts.change.decidedBy !== void 0);
|
|
107
|
+
}
|
|
108
|
+
function median(values) {
|
|
109
|
+
if (values.length === 0) return null;
|
|
110
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
111
|
+
const mid = Math.floor(sorted.length / 2);
|
|
112
|
+
return sorted.length % 2 === 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The six product metrics from ledger rows alone. One rule governs them: a
|
|
116
|
+
* change counts only after a human merged it and it stayed. Activity that
|
|
117
|
+
* never landed is not a result. Compute over rows already filtered to the
|
|
118
|
+
* period and repositories you are reporting on.
|
|
119
|
+
*/
|
|
120
|
+
function computeLedgerMetrics(rows, options = {}) {
|
|
121
|
+
const now = (options.now ?? /* @__PURE__ */ new Date()).getTime();
|
|
122
|
+
const holdMs = (options.holdDays ?? 7) * 24 * 60 * 60 * 1e3;
|
|
123
|
+
const facts = collectFacts(rows);
|
|
124
|
+
let landed = 0;
|
|
125
|
+
let provisional = 0;
|
|
126
|
+
let rolledBack = 0;
|
|
127
|
+
let closedAfterReadyCount = 0;
|
|
128
|
+
let closedInVerification = 0;
|
|
129
|
+
const landedChangeIds = /* @__PURE__ */ new Set();
|
|
130
|
+
for (const fact of facts) {
|
|
131
|
+
const { change } = fact;
|
|
132
|
+
if (fact.rolledBackAt !== null) {
|
|
133
|
+
rolledBack += 1;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (change.state === "closed") {
|
|
137
|
+
if (closedAfterReady(fact)) closedAfterReadyCount += 1;
|
|
138
|
+
else closedInVerification += 1;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (change.decidedBy !== void 0 && fact.reachedAt !== null) {
|
|
142
|
+
landed += 1;
|
|
143
|
+
landedChangeIds.add(change.id);
|
|
144
|
+
if (now - fact.reachedAt < holdMs) provisional += 1;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const stickDenominator = landed + rolledBack + closedAfterReadyCount;
|
|
148
|
+
const humanTimes = {
|
|
149
|
+
stamp: [],
|
|
150
|
+
review: []
|
|
151
|
+
};
|
|
152
|
+
for (const fact of facts) {
|
|
153
|
+
if (fact.readyAt === null || fact.mergedAt === null) continue;
|
|
154
|
+
const bucket = reviewBucket(fact.change.risk, fact.change.confidence);
|
|
155
|
+
if (bucket === "blocked") continue;
|
|
156
|
+
humanTimes[bucket].push(fact.mergedAt - fact.readyAt);
|
|
157
|
+
}
|
|
158
|
+
let settledOverLanded = 0;
|
|
159
|
+
let sawSettledCost = false;
|
|
160
|
+
for (const task of rows.tasks) {
|
|
161
|
+
if (task.settledUsd === void 0) continue;
|
|
162
|
+
if (task.kind === "code_change" && task.changeId !== void 0 && landedChangeIds.has(task.changeId)) {
|
|
163
|
+
settledOverLanded += task.settledUsd;
|
|
164
|
+
sawSettledCost = true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const rejectedSignals = rows.signals.filter((signal) => signal.outcome !== "pending" && signal.outcome !== "accepted").length;
|
|
168
|
+
return {
|
|
169
|
+
landed,
|
|
170
|
+
provisional,
|
|
171
|
+
stickRate: stickDenominator === 0 ? null : landed / stickDenominator,
|
|
172
|
+
stampedClean: null,
|
|
173
|
+
humanTimeMs: {
|
|
174
|
+
stamp: median(humanTimes.stamp),
|
|
175
|
+
review: median(humanTimes.review)
|
|
176
|
+
},
|
|
177
|
+
costPerLandedUsd: sawSettledCost && landed > 0 ? settledOverLanded / landed : null,
|
|
178
|
+
filtered: rejectedSignals + closedInVerification
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const openChangeStates = /* @__PURE__ */ new Set(["verifying", "ready"]);
|
|
182
|
+
/**
|
|
183
|
+
* Green, yellow, or red from rows, never from a model. Budget and onboarding
|
|
184
|
+
* signals are not evaluated yet: budgets do not settle and onboarding
|
|
185
|
+
* investigations are not recorded, so those spec rules have no rows to read.
|
|
186
|
+
*/
|
|
187
|
+
function computeRepositoryHealth(rows, options = {}) {
|
|
188
|
+
const now = (options.now ?? /* @__PURE__ */ new Date()).getTime();
|
|
189
|
+
const dayMs = 864e5;
|
|
190
|
+
const red = [];
|
|
191
|
+
const yellow = [];
|
|
192
|
+
for (const receipt of rows.receipts) {
|
|
193
|
+
if (now - Date.parse(receipt.recordedAt) >= dayMs) continue;
|
|
194
|
+
if (receipt.state === "rolled_back") red.push(`rollback in the last 24h (${receipt.receiptId})`);
|
|
195
|
+
if (receipt.state === "cancelled" && receipt.reason?.toLowerCase().includes("kill")) red.push(`kill in the last 24h (${receipt.receiptId})`);
|
|
196
|
+
}
|
|
197
|
+
const openQuestions = rows.tasks.filter((task) => task.kind === "question" && task.state === "needs_human").length;
|
|
198
|
+
if (openQuestions > 0) yellow.push(`${openQuestions} open question(s)`);
|
|
199
|
+
const needsHuman = rows.tasks.filter((task) => task.kind !== "question" && task.state === "needs_human").length;
|
|
200
|
+
if (needsHuman > 0) yellow.push(`${needsHuman} task(s) waiting on a human`);
|
|
201
|
+
const blocked = rows.changes.filter((change) => openChangeStates.has(change.state) && reviewBucket(change.risk, change.confidence) === "blocked").length;
|
|
202
|
+
if (blocked > 0) yellow.push(`${blocked} change(s) in the blocked bucket`);
|
|
203
|
+
if (options.maxOpenChanges !== void 0) {
|
|
204
|
+
const open = rows.changes.filter((change) => openChangeStates.has(change.state)).length;
|
|
205
|
+
if (open >= options.maxOpenChanges) yellow.push(`open changes at the cap (${open}/${options.maxOpenChanges})`);
|
|
206
|
+
}
|
|
207
|
+
if (red.length > 0) return {
|
|
208
|
+
color: "red",
|
|
209
|
+
reasons: red
|
|
210
|
+
};
|
|
211
|
+
if (yellow.length > 0) return {
|
|
212
|
+
color: "yellow",
|
|
213
|
+
reasons: yellow
|
|
214
|
+
};
|
|
215
|
+
return {
|
|
216
|
+
color: "green",
|
|
217
|
+
reasons: []
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
59
221
|
//#region src/schema/budget.ts
|
|
60
222
|
const budgetMonthSchema = z.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/);
|
|
61
223
|
const budgetSchema = z.strictObject({
|
|
@@ -86,6 +248,7 @@ const standardReceiptStates = [
|
|
|
86
248
|
"needs_human",
|
|
87
249
|
"cancelled",
|
|
88
250
|
"settled",
|
|
251
|
+
"ready",
|
|
89
252
|
"merged",
|
|
90
253
|
"deployed",
|
|
91
254
|
"rolled_out",
|
|
@@ -438,6 +601,18 @@ var ApprovalError = class extends Error {
|
|
|
438
601
|
this.reason = reason;
|
|
439
602
|
}
|
|
440
603
|
};
|
|
604
|
+
var ChangeAssociationError = class extends Error {
|
|
605
|
+
taskId;
|
|
606
|
+
repositoryId;
|
|
607
|
+
reason;
|
|
608
|
+
constructor(taskId, repositoryId, reason) {
|
|
609
|
+
super(`change for task "${taskId}" refused: ${reason}`);
|
|
610
|
+
this.name = "ChangeAssociationError";
|
|
611
|
+
this.taskId = taskId;
|
|
612
|
+
this.repositoryId = repositoryId;
|
|
613
|
+
this.reason = reason;
|
|
614
|
+
}
|
|
615
|
+
};
|
|
441
616
|
const taskReceiptStates = {
|
|
442
617
|
running: "dispatched",
|
|
443
618
|
succeeded: "succeeded",
|
|
@@ -446,6 +621,7 @@ const taskReceiptStates = {
|
|
|
446
621
|
cancelled: "cancelled"
|
|
447
622
|
};
|
|
448
623
|
const changeReceiptStates = {
|
|
624
|
+
ready: "ready",
|
|
449
625
|
merged: "merged",
|
|
450
626
|
deployed: "deployed",
|
|
451
627
|
rolled_out: "rolled_out",
|
|
@@ -688,6 +864,13 @@ function createStores(options) {
|
|
|
688
864
|
createdAt: timestamp,
|
|
689
865
|
updatedAt: timestamp
|
|
690
866
|
});
|
|
867
|
+
await mustRead("repositories", change.repositoryId, repositorySchema);
|
|
868
|
+
if (change.taskId !== void 0) {
|
|
869
|
+
const task = (await mustRead("tasks", change.taskId, taskSchema)).value;
|
|
870
|
+
if (task.kind !== "code_change") throw new ChangeAssociationError(change.taskId, change.repositoryId, "changes can only be produced by code_change tasks");
|
|
871
|
+
const taskRepositoryId = task.repositoryIds[0];
|
|
872
|
+
if (taskRepositoryId !== change.repositoryId) throw new ChangeAssociationError(change.taskId, change.repositoryId, `repository "${change.repositoryId}" does not match task repository "${taskRepositoryId}"`);
|
|
873
|
+
}
|
|
691
874
|
await driver.insert("changes", change.id, change);
|
|
692
875
|
return change;
|
|
693
876
|
},
|
|
@@ -805,6 +988,21 @@ async function sweepQueued(stores, options) {
|
|
|
805
988
|
};
|
|
806
989
|
const queued = (await stores.tasks.list()).filter((task) => task.state === "queued" && task.kind !== "question").sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
807
990
|
for (const task of queued) {
|
|
991
|
+
if (task.kind === "coordination") {
|
|
992
|
+
result.skipped.push({
|
|
993
|
+
taskId: task.id,
|
|
994
|
+
reason: "coordination tasks are reconciled, not dispatched"
|
|
995
|
+
});
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
const executionRepositoryIds = taskExecutionRepositoryIdsSchema.safeParse(task.repositoryIds);
|
|
999
|
+
if (!executionRepositoryIds.success) {
|
|
1000
|
+
result.skipped.push({
|
|
1001
|
+
taskId: task.id,
|
|
1002
|
+
reason: "task execution requires exactly one repository"
|
|
1003
|
+
});
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
808
1006
|
if (task.approval === "required") {
|
|
809
1007
|
result.skipped.push({
|
|
810
1008
|
taskId: task.id,
|
|
@@ -820,8 +1018,7 @@ async function sweepQueued(stores, options) {
|
|
|
820
1018
|
});
|
|
821
1019
|
continue;
|
|
822
1020
|
}
|
|
823
|
-
const
|
|
824
|
-
const repository = repositoryId === void 0 ? null : await stores.repositories.get(repositoryId);
|
|
1021
|
+
const repository = await stores.repositories.get(executionRepositoryIds.data[0]);
|
|
825
1022
|
if (repository === null) {
|
|
826
1023
|
result.skipped.push({
|
|
827
1024
|
taskId: task.id,
|
|
@@ -849,6 +1046,6 @@ async function sweepQueued(stores, options) {
|
|
|
849
1046
|
return result;
|
|
850
1047
|
}
|
|
851
1048
|
//#endregion
|
|
852
|
-
export { ApprovalError, DEFAULT_MAX_LAUNCH_ATTEMPTS, DispatchRefusedError, InvalidTransitionError, RecordExistsError, RecordNotFoundError, SCHEMA_VERSION, VersionConflictError, adoptPullRequest, approvalPolicySchema, approvalRuleSchema, assertTransition, budgetMonthSchema, budgetSchema, changeEvidenceSchema, changeIdSchema, changeInitialState, changeSchema, changeStateSchema, changeTransitions, costSchema, createInMemoryDriver, createSignalCreator, createStores, defaultApprovalPolicy, defineFactory, dispatchTask, factoryConfigRepositorySchema, factoryConfigSchema, factoryIdSchema, findingCategorySchema, findingFingerprint, findingForTask, findingPrioritySchema, findingSchema, findingVerificationLevelSchema, findingVerificationMethodSchema, findingVerificationSchema, fixBrief, harnessSchema, isTransitionAllowed, isoDateTimeSchema, legalNextStates, levelSchema, receiptIdSchema, receiptKindSchema, receiptPhaseSchema, receiptSchema, receiptStateSchema, replyToSchema, repositoryIdSchema, repositoryIdsSchema, repositorySchema, repositorySlugSchema, requiresApproval, scanBrief, schemaVersionSchema, signalIdSchema, signalInitialOutcome, signalOutcomeSchema, signalOutcomeTransitions, signalSchema, signalSourceSchema, standardReceiptKinds, standardReceiptPhases, standardReceiptStates, standardSignalSources, standardTaskKinds, sweepQueued, taskApprovalSchema, taskDevboxSchema, taskIdSchema, taskInitialState, taskKindSchema, taskOriginSchema, taskQuestionSchema, taskSchema, taskStateSchema, taskTransitions, terminalStates, usdSchema, vocabularySchema };
|
|
1049
|
+
export { ApprovalError, ChangeAssociationError, DEFAULT_MAX_LAUNCH_ATTEMPTS, DispatchRefusedError, InvalidTransitionError, RecordExistsError, RecordNotFoundError, SCHEMA_VERSION, VersionConflictError, adoptPullRequest, approvalPolicySchema, approvalRuleSchema, assertTransition, budgetMonthSchema, budgetSchema, changeEvidenceSchema, changeIdSchema, changeInitialState, changeSchema, changeStateSchema, changeTransitions, computeLedgerMetrics, computeRepositoryHealth, coordinationRepositoryIdsSchema, costSchema, createInMemoryDriver, createSignalCreator, createStores, defaultApprovalPolicy, defineFactory, dispatchTask, factoryConfigRepositorySchema, factoryConfigSchema, factoryIdSchema, findingCategorySchema, findingFingerprint, findingForTask, findingPrioritySchema, findingSchema, findingVerificationLevelSchema, findingVerificationMethodSchema, findingVerificationSchema, fixBrief, harnessSchema, isTransitionAllowed, isoDateTimeSchema, legalNextStates, levelSchema, receiptIdSchema, receiptKindSchema, receiptPhaseSchema, receiptSchema, receiptStateSchema, replyToSchema, repositoryIdSchema, repositoryIdsSchema, repositorySchema, repositorySlugSchema, requiresApproval, reviewBucket, scanBrief, schemaVersionSchema, signalIdSchema, signalInitialOutcome, signalOutcomeSchema, signalOutcomeTransitions, signalSchema, signalSourceSchema, standardReceiptKinds, standardReceiptPhases, standardReceiptStates, standardSignalSources, standardTaskKinds, sweepQueued, taskApprovalSchema, taskDevboxSchema, taskExecutionRepositoryIdsSchema, taskIdSchema, taskInitialState, taskKindSchema, taskOriginSchema, taskQuestionSchema, taskSchema, taskStateSchema, taskTransitions, terminalStates, usdSchema, vocabularySchema };
|
|
853
1050
|
|
|
854
1051
|
//# sourceMappingURL=index.mjs.map
|