omp-conductor 0.17.1 → 0.18.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/REFERENCE.md +11 -7
- package/package.json +1 -1
- package/schema/config.schema.json +24 -1
- package/src/admission.ts +104 -1
- package/src/ask.ts +39 -3
- package/src/backups.ts +2 -2
- package/src/briefs/orchestrator.md +1 -0
- package/src/briefs/worker.md +36 -19
- package/src/command-help.ts +8 -1
- package/src/command-manifest.ts +5 -2
- package/src/commands/watch.ts +62 -3
- package/src/config-schema.ts +33 -0
- package/src/config.ts +60 -1
- package/src/daemon.ts +253 -1479
- package/src/decisions.ts +51 -6
- package/src/depends-on.ts +261 -1
- package/src/diff-flags.ts +350 -0
- package/src/digest-schedule.ts +37 -0
- package/src/failure-class.ts +15 -2
- package/src/fleet.ts +20 -2
- package/src/graph-health.ts +20 -7
- package/src/graph.ts +313 -68
- package/src/lifecycle.ts +36 -5
- package/src/omp.ts +42 -0
- package/src/orchestrator-tick.ts +104 -12
- package/src/routing.ts +11 -3
- package/src/session-host.ts +16 -0
- package/src/settlement.ts +1728 -0
- package/src/setup-install.ts +91 -30
- package/src/setup-wizard.ts +75 -0
- package/src/setup.ts +93 -3
- package/src/status-render.ts +31 -9
- package/src/store.ts +78 -5
- package/src/tracker/github.ts +46 -0
- package/src/types.ts +127 -3
- package/src/verbs/server.ts +58 -2
- package/src/worker.ts +214 -7
- package/src/worktree.ts +115 -8
package/src/config.ts
CHANGED
|
@@ -28,6 +28,9 @@ import {
|
|
|
28
28
|
DEFAULT_PROJECT_POLICY,
|
|
29
29
|
DEFAULT_REPORT_POLICY,
|
|
30
30
|
DEFAULT_REPORT_SCOPE,
|
|
31
|
+
DEFAULT_REVIEW_MAX_ROUNDS,
|
|
32
|
+
DEFAULT_REVIEW_POLICY,
|
|
33
|
+
DEFAULT_REVIEW_STRICTNESS,
|
|
31
34
|
DENIED_RELEASE_GRANTS,
|
|
32
35
|
DRAFT_POLICIES,
|
|
33
36
|
LEGACY_RELEASE_POLICIES,
|
|
@@ -37,6 +40,9 @@ import {
|
|
|
37
40
|
RELEASE_REQUIREMENTS,
|
|
38
41
|
RELEASE_SHAPES,
|
|
39
42
|
REPORT_SCOPES,
|
|
43
|
+
REVIEW_MAX_ROUNDS_MAX,
|
|
44
|
+
REVIEW_MAX_ROUNDS_MIN,
|
|
45
|
+
REVIEW_STRICTNESS,
|
|
40
46
|
INTERRUPT_CATEGORIES,
|
|
41
47
|
DIGEST_CADENCES,
|
|
42
48
|
WEEKDAYS,
|
|
@@ -53,6 +59,8 @@ import {
|
|
|
53
59
|
type ReleaseRequirement,
|
|
54
60
|
type ReportScope,
|
|
55
61
|
type ReportingPolicy,
|
|
62
|
+
type ReviewPolicy,
|
|
63
|
+
type ReviewStrictness,
|
|
56
64
|
type Weekday,
|
|
57
65
|
type WeeklyAvailability,
|
|
58
66
|
type RepoTarget,
|
|
@@ -71,6 +79,7 @@ import {
|
|
|
71
79
|
ORCHESTRATOR_MODE_LIST,
|
|
72
80
|
RELEASE_REQUIREMENT_LIST,
|
|
73
81
|
RELEASE_SHAPE_LIST,
|
|
82
|
+
REVIEW_STRICTNESS_LIST,
|
|
74
83
|
REPORT_SCOPE_LIST,
|
|
75
84
|
WEEKDAY_LIST,
|
|
76
85
|
quoteList,
|
|
@@ -98,10 +107,13 @@ const REPO_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
|
98
107
|
* Used when a project omits `stateLabels`. Namespaced so a human scanning the
|
|
99
108
|
* tracker can tell dispatcher-written labels from their own.
|
|
100
109
|
*/
|
|
101
|
-
const DEFAULT_STATE_LABELS: ProjectConfig["stateLabels"] = {
|
|
110
|
+
export const DEFAULT_STATE_LABELS: ProjectConfig["stateLabels"] = {
|
|
102
111
|
inProgress: "agent:in-progress",
|
|
103
112
|
blocked: "agent:blocked",
|
|
104
113
|
failed: "agent:failed",
|
|
114
|
+
// The operator's park gesture (#507): unnamed by design — it is theirs, and
|
|
115
|
+
// the operator adds it by hand until setup grows a confirm step (slice 2).
|
|
116
|
+
backlog: "backlog",
|
|
105
117
|
};
|
|
106
118
|
|
|
107
119
|
/**
|
|
@@ -330,6 +342,20 @@ export function resolveArmProof(p: ProjectConfig): ArmProof {
|
|
|
330
342
|
return p.arm?.proof === "claim-only" ? "claim-only" : DEFAULT_ARM_PROOF;
|
|
331
343
|
}
|
|
332
344
|
|
|
345
|
+
/**
|
|
346
|
+
* The project's review policy (#678), complete.
|
|
347
|
+
*
|
|
348
|
+
* The loader always materialises `review` (finalizeProject), so this is only
|
|
349
|
+
* ever the fallback for a `ProjectConfig` that never went through it — a
|
|
350
|
+
* hand-built one in a test, or a config written before the key existed.
|
|
351
|
+
* Absent resolves to {@link DEFAULT_REVIEW_POLICY}, the documented migration
|
|
352
|
+
* default every existing install deterministically upgrades to. A fresh object
|
|
353
|
+
* each call, so mutating the result cannot reach the config it came from.
|
|
354
|
+
*/
|
|
355
|
+
export function resolveReview(p: ProjectConfig): ReviewPolicy {
|
|
356
|
+
return { ...(p.review ?? DEFAULT_REVIEW_POLICY) };
|
|
357
|
+
}
|
|
358
|
+
|
|
333
359
|
/**
|
|
334
360
|
* A policy with no array shared with its source.
|
|
335
361
|
*
|
|
@@ -861,6 +887,12 @@ function clauseFor(rel: readonly PropertyKey[], issue: {
|
|
|
861
887
|
if (rel.length === 1 && relStr === "policy") {
|
|
862
888
|
return `policy must be an object with "merge" and "release" sections`;
|
|
863
889
|
}
|
|
890
|
+
if (rel.length === 1 && relStr === "review") {
|
|
891
|
+
return `review must be an object with "strictness" and "maxRounds"`;
|
|
892
|
+
}
|
|
893
|
+
if (rel.length === 2 && rel[0] === "review" && rel[1] === "maxRounds") {
|
|
894
|
+
return `review.maxRounds must be an integer between ${REVIEW_MAX_ROUNDS_MIN} and ${REVIEW_MAX_ROUNDS_MAX}, found ${found()}`;
|
|
895
|
+
}
|
|
864
896
|
if (rel.length === 2 && relStr === "policy") return `${pathStr} must be an object`;
|
|
865
897
|
if (rel.length === 1 && relStr === "caps") return `caps must be an object`;
|
|
866
898
|
if (rel.length === 2 && relStr === "caps" && rel[1] === "planUsage") {
|
|
@@ -911,6 +943,7 @@ function clauseFor(rel: readonly PropertyKey[], issue: {
|
|
|
911
943
|
if (issue.code === "unrecognized_keys") {
|
|
912
944
|
const keys = (issue.keys ?? []).join(", ");
|
|
913
945
|
if (pathStr === "policy") return `policy has unknown key(s): ${keys} — expected "merge" or "release"`;
|
|
946
|
+
if (pathStr === "review") return `review has unknown key(s): ${keys} — expected "strictness" and "maxRounds"`;
|
|
914
947
|
if (pathStr === "policy.merge") return `${pathStr} has unknown key(s): ${keys} — expected ${POLICY_MERGE_KEYS}`;
|
|
915
948
|
if (pathStr === "policy.release") return `${pathStr} has unknown key(s): ${keys} — expected ${POLICY_RELEASE_KEYS}`;
|
|
916
949
|
if (pathStr === "caps.planUsage") {
|
|
@@ -960,6 +993,9 @@ function clauseFor(rel: readonly PropertyKey[], issue: {
|
|
|
960
993
|
|
|
961
994
|
// Regex / custom-message failures carry the finding in issue.message.
|
|
962
995
|
if (issue.code === "too_small") {
|
|
996
|
+
if (pathStr === "review.maxRounds") {
|
|
997
|
+
return `review.maxRounds must be an integer between ${REVIEW_MAX_ROUNDS_MIN} and ${REVIEW_MAX_ROUNDS_MAX}, found ${found()}`;
|
|
998
|
+
}
|
|
963
999
|
// Empty array (reporting.interruptOn, availability.days) has its own line.
|
|
964
1000
|
if (parentOf(rel) === "reporting.interruptOn") return `reporting.interruptOn must name at least one category`;
|
|
965
1001
|
if (pathStr === "reporting.availability.days") return `reporting.availability.days must be a non-empty array of ${WEEKDAY_LIST}`;
|
|
@@ -975,6 +1011,9 @@ function clauseFor(rel: readonly PropertyKey[], issue: {
|
|
|
975
1011
|
}
|
|
976
1012
|
|
|
977
1013
|
if (issue.code === "too_big") {
|
|
1014
|
+
if (pathStr === "review.maxRounds") {
|
|
1015
|
+
return `review.maxRounds must be an integer between ${REVIEW_MAX_ROUNDS_MIN} and ${REVIEW_MAX_ROUNDS_MAX}, found ${found()}`;
|
|
1016
|
+
}
|
|
978
1017
|
if (rel.length === 2 && rel[0] === "caps") return capProblem(rel[1] as string, found());
|
|
979
1018
|
if (pathStr === "caps.planUsage.maxUsedFraction") {
|
|
980
1019
|
return `caps.planUsage.maxUsedFraction must be a fraction between 0 and 1 (0.85 holds at 85% of the allowance), found ${found()}`;
|
|
@@ -1101,6 +1140,7 @@ function finalizeProject(
|
|
|
1101
1140
|
);
|
|
1102
1141
|
}
|
|
1103
1142
|
const policy = finalizePolicy(p["policy"], label, problems);
|
|
1143
|
+
const review = finalizeReview(p["review"] as Raw | undefined);
|
|
1104
1144
|
const caps = reconcileCaps(p["caps"], `${label}: caps`, problems, legacyCaps);
|
|
1105
1145
|
const reporting = finalizeReporting(p["reporting"], label, problems);
|
|
1106
1146
|
const recoveryMerges = (p["recoveryMerges"] as RecoveryMergeAuthorization[] | undefined)?.map(
|
|
@@ -1199,6 +1239,7 @@ function finalizeProject(
|
|
|
1199
1239
|
inProgress: pickString(stateLabels?.["inProgress"], DEFAULT_STATE_LABELS.inProgress),
|
|
1200
1240
|
blocked: pickString(stateLabels?.["blocked"], DEFAULT_STATE_LABELS.blocked),
|
|
1201
1241
|
failed: pickString(stateLabels?.["failed"], DEFAULT_STATE_LABELS.failed),
|
|
1242
|
+
backlog: pickString(stateLabels?.["backlog"], DEFAULT_STATE_LABELS.backlog),
|
|
1202
1243
|
},
|
|
1203
1244
|
routing: { labelPrefix, repos },
|
|
1204
1245
|
caps,
|
|
@@ -1208,6 +1249,7 @@ function finalizeProject(
|
|
|
1208
1249
|
...(effectiveOmpSettings === undefined ? {} : { ompSettings: effectiveOmpSettings }),
|
|
1209
1250
|
escalation,
|
|
1210
1251
|
arm,
|
|
1252
|
+
review,
|
|
1211
1253
|
authority,
|
|
1212
1254
|
releasePolicy,
|
|
1213
1255
|
policy,
|
|
@@ -1231,6 +1273,23 @@ function finalizeArm(parsed: Raw | undefined): ProjectConfig["arm"] {
|
|
|
1231
1273
|
return { proof: (parsed["proof"] as ArmProof | undefined) ?? DEFAULT_ARM_PROOF };
|
|
1232
1274
|
}
|
|
1233
1275
|
|
|
1276
|
+
/**
|
|
1277
|
+
* The project's review policy (#678), complete. A config written before the
|
|
1278
|
+
* key existed — or one that never answered — materialises as
|
|
1279
|
+
* {@link DEFAULT_REVIEW_POLICY}, the documented migration default, so an
|
|
1280
|
+
* upgrade changes no existing project's behaviour beyond what its operators
|
|
1281
|
+
* then choose in setup. zod has already rejected anything outside the
|
|
1282
|
+
* strictness vocabulary or the validated round range, so this only fills the
|
|
1283
|
+
* absent-case default.
|
|
1284
|
+
*/
|
|
1285
|
+
function finalizeReview(parsed: Raw | undefined): ProjectConfig["review"] {
|
|
1286
|
+
if (parsed === undefined) return { ...DEFAULT_REVIEW_POLICY };
|
|
1287
|
+
return {
|
|
1288
|
+
strictness: (parsed["strictness"] as ReviewStrictness | undefined) ?? DEFAULT_REVIEW_STRICTNESS,
|
|
1289
|
+
maxRounds: (parsed["maxRounds"] as number | undefined) ?? DEFAULT_REVIEW_MAX_ROUNDS,
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1234
1293
|
function finalizeEscalation(parsed: Raw | undefined): ProjectConfig["escalation"] {
|
|
1235
1294
|
if (parsed === undefined) {
|
|
1236
1295
|
return { fallbackToIssueComment: true, orchestrator: "embedded" };
|