omp-conductor 0.13.0 → 0.15.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 +549 -234
- package/package.json +8 -5
- package/schema/config.schema.json +609 -0
- package/src/availability.ts +165 -0
- package/src/board.ts +19 -32
- package/src/brief-upgrade.ts +1 -1
- package/src/briefs/orchestrator.md +72 -31
- package/src/briefs/policy.md +48 -36
- package/src/briefs/probes/gates.md +51 -0
- package/src/briefs/probes/project-context.md +59 -0
- package/src/briefs/probes/release-procedure.md +81 -0
- package/src/cli.ts +356 -212
- package/src/config-schema.ts +352 -0
- package/src/config.ts +1037 -679
- package/src/confinement.ts +54 -0
- package/src/daemon.ts +644 -390
- package/src/diff-flags.ts +73 -4
- package/src/digest-schedule.ts +92 -24
- package/src/escalate.ts +89 -22
- package/src/fleet.ts +351 -46
- package/src/generate-schema.ts +21 -0
- package/src/graph.ts +3 -3
- package/src/host.ts +16 -0
- package/src/omp.ts +21 -1
- package/src/orchestrator-tick.ts +732 -56
- package/src/privileged.ts +264 -0
- package/src/reports.ts +203 -6
- package/src/session-host.ts +3 -0
- package/src/setup-host.ts +209 -24
- package/src/setup-install.ts +320 -0
- package/src/setup-probe.ts +412 -0
- package/src/setup-wizard.ts +1946 -0
- package/src/setup.ts +457 -53
- package/src/store.ts +610 -98
- package/src/tracker/github.ts +43 -5
- package/src/types.ts +153 -14
- package/src/upgrade.ts +44 -10
- package/src/verbs/actions.ts +131 -13
- package/src/verbs/server.ts +40 -18
- package/src/wizard-ui.ts +249 -0
- package/src/worker.ts +24 -7
- package/skills/conductor-onboarding/SKILL.md +0 -748
- package/skills/conductor-update/SKILL.md +0 -51
- package/src/plugin.ts +0 -1495
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The zod schema for the on-disk conductor config, and the JSON Schema it
|
|
3
|
+
* renders.
|
|
4
|
+
*
|
|
5
|
+
* `ConductorConfig`'s vocabulary was validated field by field by hand-written
|
|
6
|
+
* guards in `config.ts`. This file is the upgrade those guards always pointed
|
|
7
|
+
* at (see the note on `Raw` in `config.ts`): the shape, the value types and
|
|
8
|
+
* every closed enum are now a declarative schema built from the same exported
|
|
9
|
+
* `as const` arrays `types.ts` publishes, so the on-disk vocabulary, the load
|
|
10
|
+
* gate and the shipped `config.schema.json` cannot fork.
|
|
11
|
+
*
|
|
12
|
+
* This schema is the per-field authority: types, enums, numeric bounds,
|
|
13
|
+
* required/optional, and defaults all live here. What it deliberately does
|
|
14
|
+
* **not** express — because the loader must, and zod cannot cleanly — is left
|
|
15
|
+
* to the residue normalisers in `config.ts`: cross-field coherence
|
|
16
|
+
* (scope-vs-explicit reporting, digest/availability times, `start !== end`),
|
|
17
|
+
* the version-keyed cap migration, the clone-URL credential rejection, and
|
|
18
|
+
* `~`/default path expansion. Editing a schema node here changes both what
|
|
19
|
+
* `loadConfig` accepts and the shipped schema; `bun test`'s freshness lock
|
|
20
|
+
* fails if the checked-in JSON is not regenerated.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
import {
|
|
25
|
+
AUTHORITY_HOLDERS,
|
|
26
|
+
BASE_FRESHNESS,
|
|
27
|
+
BEHIND_BASE_ACTIONS,
|
|
28
|
+
CONFIG_VERSION,
|
|
29
|
+
DEFAULT_AUTHORITY,
|
|
30
|
+
DEFAULT_CAPS,
|
|
31
|
+
DEFAULT_PROJECT_POLICY,
|
|
32
|
+
DRAFT_POLICIES,
|
|
33
|
+
INTERRUPT_CATEGORIES,
|
|
34
|
+
LEGACY_RELEASE_POLICIES,
|
|
35
|
+
ORCHESTRATOR_MODES,
|
|
36
|
+
READABLE_CONFIG_VERSIONS,
|
|
37
|
+
RELEASE_REQUIREMENTS,
|
|
38
|
+
RELEASE_SHAPES,
|
|
39
|
+
REPORT_SCOPES,
|
|
40
|
+
WEEKDAYS,
|
|
41
|
+
DIGEST_CADENCES,
|
|
42
|
+
} from "./types.ts";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The quoted-literal spelling the loader uses in error text (`"a" or "b"`),
|
|
46
|
+
* so a message built from these vocabularies cannot fork from the values it
|
|
47
|
+
* names. Kept here (not in `config.ts`) because `config.ts` imports this file
|
|
48
|
+
* at the parse boundary.
|
|
49
|
+
*/
|
|
50
|
+
export function quoteList(values: readonly string[]): string {
|
|
51
|
+
return values.map((v) => `"${v}"`).join(" or ");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const REPORT_SCOPE_LIST = quoteList(REPORT_SCOPES);
|
|
55
|
+
const INTERRUPT_CATEGORY_LIST = quoteList(INTERRUPT_CATEGORIES);
|
|
56
|
+
const WEEKDAY_LIST = quoteList(WEEKDAYS);
|
|
57
|
+
const DIGEST_CADENCE_LIST = quoteList(DIGEST_CADENCES);
|
|
58
|
+
const AUTHORITY_HOLDER_LIST = quoteList(AUTHORITY_HOLDERS);
|
|
59
|
+
const BASE_FRESHNESS_LIST = quoteList(BASE_FRESHNESS);
|
|
60
|
+
const DRAFT_POLICY_LIST = quoteList(DRAFT_POLICIES);
|
|
61
|
+
const BEHIND_BASE_ACTION_LIST = quoteList(BEHIND_BASE_ACTIONS);
|
|
62
|
+
const RELEASE_REQUIREMENT_LIST = quoteList(RELEASE_REQUIREMENTS);
|
|
63
|
+
const RELEASE_SHAPE_LIST = quoteList(RELEASE_SHAPES);
|
|
64
|
+
const ORCHESTRATOR_MODE_LIST = quoteList(ORCHESTRATOR_MODES);
|
|
65
|
+
const LEGACY_RELEASE_POLICY_LIST = quoteList(LEGACY_RELEASE_POLICIES);
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Closed vocabularies — every one built from the exported `as const` array in
|
|
69
|
+
// types.ts so a new member is a schema change too, never a silent fork.
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
const reportScopeEnum = z.enum([...REPORT_SCOPES]);
|
|
73
|
+
const interruptCategoryEnum = z.enum([...INTERRUPT_CATEGORIES]);
|
|
74
|
+
const weekdayEnum = z.enum([...WEEKDAYS]);
|
|
75
|
+
const digestCadenceEnum = z.enum([...DIGEST_CADENCES]);
|
|
76
|
+
const authorityHolderEnum = z.enum([...AUTHORITY_HOLDERS]);
|
|
77
|
+
const baseFreshnessEnum = z.enum([...BASE_FRESHNESS]);
|
|
78
|
+
const draftPolicyEnum = z.enum([...DRAFT_POLICIES]);
|
|
79
|
+
const behindBaseActionEnum = z.enum([...BEHIND_BASE_ACTIONS]);
|
|
80
|
+
const releaseRequirementEnum = z.enum([...RELEASE_REQUIREMENTS]);
|
|
81
|
+
const releaseShapeEnum = z.enum([...RELEASE_SHAPES]);
|
|
82
|
+
const orchestratorModeEnum = z.enum([...ORCHESTRATOR_MODES]);
|
|
83
|
+
const releasePolicyLegacyEnum = z.enum([...LEGACY_RELEASE_POLICIES]);
|
|
84
|
+
|
|
85
|
+
/** The 24-hour `HH:MM` shape `digest.at` / `availability.start/end` take. */
|
|
86
|
+
const HHMM = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
87
|
+
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// Caps
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
const planUsageCap = z
|
|
93
|
+
.object({
|
|
94
|
+
windowId: z
|
|
95
|
+
.string()
|
|
96
|
+
.min(1, "must be a non-empty allowance id such as \"anthropic:7d\"")
|
|
97
|
+
.describe("The allowance window this threshold guards"),
|
|
98
|
+
maxUsedFraction: z
|
|
99
|
+
.number()
|
|
100
|
+
.min(0)
|
|
101
|
+
.max(1)
|
|
102
|
+
|
|
103
|
+
.describe("Consumed share, 0–1, at which new claims stop"),
|
|
104
|
+
})
|
|
105
|
+
.strict()
|
|
106
|
+
.describe("An allowance-window spend guard, or null for unmetered");
|
|
107
|
+
|
|
108
|
+
const capsSchema = z
|
|
109
|
+
.object({
|
|
110
|
+
maxConcurrentWorkers: z.number().min(0).describe(`Parallel omp sessions (default ${DEFAULT_CAPS.maxConcurrentWorkers})`),
|
|
111
|
+
maxConcurrentWorkersPerRepo: z.number().min(0).describe(`Max live workers per repository (default ${DEFAULT_CAPS.maxConcurrentWorkersPerRepo})`),
|
|
112
|
+
dailySpendUsd: z
|
|
113
|
+
.number()
|
|
114
|
+
.min(0)
|
|
115
|
+
.nullable()
|
|
116
|
+
.describe(`Rolling-day spend ceiling; null means no spend gate (default ${DEFAULT_CAPS.dailySpendUsd})`),
|
|
117
|
+
planUsage: z.union([planUsageCap, z.null()]).describe(`Plan-allowance guard, or null for unmetered`),
|
|
118
|
+
workerMaxTurns: z.number().min(0).describe(`Turn ceiling for one worker (default ${DEFAULT_CAPS.workerMaxTurns})`),
|
|
119
|
+
workerMaxTurnsCeiling: z.number().min(0).describe(`Maximum turn budget assignable to one issue's next attempt`),
|
|
120
|
+
workerWallClockMs: z.number().min(0).describe(`Wall-clock ceiling for one worker (default ${DEFAULT_CAPS.workerWallClockMs})`),
|
|
121
|
+
maxAttemptsPerIssue: z.number().min(0).describe(`Failed attempts allowed before escalation (default ${DEFAULT_CAPS.maxAttemptsPerIssue})`),
|
|
122
|
+
maxContinuationsPerIssue: z.number().min(0).describe(`Operational continuations allowed before crash/resume escalation (default ${DEFAULT_CAPS.maxContinuationsPerIssue})`),
|
|
123
|
+
})
|
|
124
|
+
// A cap object carries any subset (project overrides are partial) and may
|
|
125
|
+
// carry retired keys (v1) that the load boundary drops or rejects by version,
|
|
126
|
+
// so unknown keys are kept and reconciled in `config.ts`, never rejected here.
|
|
127
|
+
.partial()
|
|
128
|
+
.loose()
|
|
129
|
+
.describe("Per-fleet or per-project hard limits");
|
|
130
|
+
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// Reporting policy
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
const availabilitySchema = z
|
|
136
|
+
.object({
|
|
137
|
+
timezone: z.string().min(1).describe("IANA timezone of the window"),
|
|
138
|
+
days: z.array(weekdayEnum).min(1).describe("Days the window is open"),
|
|
139
|
+
start: z.string().regex(HHMM, "must be a 24h HH:MM time").describe("Window open time, operator-local"),
|
|
140
|
+
end: z.string().regex(HHMM, "must be a 24h HH:MM time").describe("Window close time, operator-local"),
|
|
141
|
+
bypass: z.array(interruptCategoryEnum).describe("Categories that may interrupt outside the window").optional(),
|
|
142
|
+
})
|
|
143
|
+
.strict()
|
|
144
|
+
.superRefine((a, ctx) => {
|
|
145
|
+
// Presence is required on disk (unlike a defaultable field); report the
|
|
146
|
+
// missing key in the loader's own voice rather than zod's.
|
|
147
|
+
if (a.bypass === undefined) {
|
|
148
|
+
ctx.addIssue({
|
|
149
|
+
code: "custom",
|
|
150
|
+
path: ["bypass"],
|
|
151
|
+
message: `must be an array of ${INTERRUPT_CATEGORY_LIST} (empty means none)`,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const digestSchema = z
|
|
157
|
+
.object({
|
|
158
|
+
cadence: digestCadenceEnum.describe("When the daily/report rollup is due"),
|
|
159
|
+
at: z.string().regex(HHMM, "must be a 24h HH:MM time").optional().describe("Daily rollup time"),
|
|
160
|
+
timezone: z.string().describe("IANA timezone of the daily rollup").optional(),
|
|
161
|
+
})
|
|
162
|
+
.strict()
|
|
163
|
+
.describe("The reporting digest/rollup schedule");
|
|
164
|
+
|
|
165
|
+
const reportingSchema = z
|
|
166
|
+
.object({
|
|
167
|
+
scope: reportScopeEnum.optional().describe("Legacy reporting preset; mutually exclusive with the explicit form"),
|
|
168
|
+
scopePreset: reportScopeEnum.optional().describe("Back-annotation of the legacy preset this policy came from"),
|
|
169
|
+
interruptOn: z.array(interruptCategoryEnum).optional().describe("Which categories interrupt the operator's phone"),
|
|
170
|
+
digest: digestSchema.optional(),
|
|
171
|
+
availability: availabilitySchema.optional(),
|
|
172
|
+
})
|
|
173
|
+
.strict();
|
|
174
|
+
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// Merge / release preconditions
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
const nameListSchema = z.array(z.string().min(1));
|
|
180
|
+
|
|
181
|
+
const mergePreconditionsSchema = z
|
|
182
|
+
.object({
|
|
183
|
+
requiredChecks: nameListSchema.default(DEFAULT_PROJECT_POLICY.merge.requiredChecks),
|
|
184
|
+
baseFreshness: baseFreshnessEnum.default(DEFAULT_PROJECT_POLICY.merge.baseFreshness),
|
|
185
|
+
drafts: draftPolicyEnum.default(DEFAULT_PROJECT_POLICY.merge.drafts),
|
|
186
|
+
whenBehindBase: behindBaseActionEnum.default(DEFAULT_PROJECT_POLICY.merge.whenBehindBase),
|
|
187
|
+
})
|
|
188
|
+
.strict();
|
|
189
|
+
|
|
190
|
+
const releasePreconditionsSchema = z
|
|
191
|
+
.object({
|
|
192
|
+
requires: z.array(releaseRequirementEnum).default(DEFAULT_PROJECT_POLICY.release.requires),
|
|
193
|
+
requiredChecks: nameListSchema.default(DEFAULT_PROJECT_POLICY.release.requiredChecks),
|
|
194
|
+
artefacts: nameListSchema.default(DEFAULT_PROJECT_POLICY.release.artefacts),
|
|
195
|
+
environments: nameListSchema.default(DEFAULT_PROJECT_POLICY.release.environments),
|
|
196
|
+
})
|
|
197
|
+
.strict();
|
|
198
|
+
|
|
199
|
+
const projectPolicySchema = z
|
|
200
|
+
.object({
|
|
201
|
+
merge: mergePreconditionsSchema.optional(),
|
|
202
|
+
release: releasePreconditionsSchema.optional(),
|
|
203
|
+
})
|
|
204
|
+
.strict();
|
|
205
|
+
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// Authority / escalation / release policy
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
|
|
210
|
+
const authoritySchema = z
|
|
211
|
+
.object({
|
|
212
|
+
merge: authorityHolderEnum.default(DEFAULT_AUTHORITY.merge),
|
|
213
|
+
release: authorityHolderEnum.default(DEFAULT_AUTHORITY.release),
|
|
214
|
+
})
|
|
215
|
+
.strict();
|
|
216
|
+
|
|
217
|
+
const escalationSchema = z
|
|
218
|
+
.object({
|
|
219
|
+
telegramChatId: z.unknown().optional(),
|
|
220
|
+
telegramTopicId: z.unknown().optional(),
|
|
221
|
+
fallbackToIssueComment: z.unknown().default(true),
|
|
222
|
+
orchestrator: orchestratorModeEnum.default("embedded"),
|
|
223
|
+
})
|
|
224
|
+
.strict();
|
|
225
|
+
|
|
226
|
+
const releasePolicySchema = z.union([
|
|
227
|
+
releasePolicyLegacyEnum,
|
|
228
|
+
z.record(z.string(), authorityHolderEnum),
|
|
229
|
+
]);
|
|
230
|
+
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
// Repo targets
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
const gateSchema = z
|
|
236
|
+
.object({
|
|
237
|
+
cmd: z.string().optional(),
|
|
238
|
+
cwd: z.unknown().default("."),
|
|
239
|
+
})
|
|
240
|
+
.superRefine((g, ctx) => {
|
|
241
|
+
if (typeof g.cmd !== "string" || g.cmd.trim() === "") {
|
|
242
|
+
ctx.addIssue({ code: "custom", message: `must be { cmd, cwd } with a non-empty cmd` });
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const gatesSchema = z.array(gateSchema);
|
|
247
|
+
|
|
248
|
+
const repoTargetSchema = z
|
|
249
|
+
.object({
|
|
250
|
+
name: z.unknown().optional(),
|
|
251
|
+
cloneUrl: z.string().min(1, "must be a non-empty string"),
|
|
252
|
+
defaultBranch: z.unknown().default("main").optional(),
|
|
253
|
+
gates: gatesSchema.optional(),
|
|
254
|
+
graphProject: z.string().optional(),
|
|
255
|
+
migrations: z
|
|
256
|
+
.object({ dir: z.string().min(1, "must be a non-empty string") })
|
|
257
|
+
.strict()
|
|
258
|
+
.optional(),
|
|
259
|
+
})
|
|
260
|
+
.strict();
|
|
261
|
+
|
|
262
|
+
const stateLabelsSchema = z
|
|
263
|
+
.object({
|
|
264
|
+
// The loader defaults each of these (pickString), so a malformed value is
|
|
265
|
+
// tolerated rather than rejected — the schema admits any shape and lets the
|
|
266
|
+
// normaliser decide what is usable.
|
|
267
|
+
inProgress: z.unknown(),
|
|
268
|
+
blocked: z.unknown(),
|
|
269
|
+
failed: z.unknown(),
|
|
270
|
+
})
|
|
271
|
+
.partial();
|
|
272
|
+
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
// Project / root
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
|
|
277
|
+
const routingSchema = z.object({
|
|
278
|
+
labelPrefix: z.unknown().optional(),
|
|
279
|
+
repos: z
|
|
280
|
+
.record(z.string(), repoTargetSchema)
|
|
281
|
+
.refine((r) => Object.keys(r).length > 0, `routing.repos needs at least one repo entry, or no issue can be routed`),
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const projectSchema = z
|
|
285
|
+
.object({
|
|
286
|
+
name: z.string().min(1, "must be a non-empty string"),
|
|
287
|
+
tracker: z
|
|
288
|
+
.object({
|
|
289
|
+
kind: z.literal("github").optional(),
|
|
290
|
+
repo: z.string().regex(/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/, `must look like "owner/repo"`),
|
|
291
|
+
})
|
|
292
|
+
.strict(),
|
|
293
|
+
queueLabel: z.string().min(1, "must be a non-empty string — it is the human sign-off gate"),
|
|
294
|
+
groomBelow: z.unknown().optional(),
|
|
295
|
+
stateLabels: stateLabelsSchema.optional(),
|
|
296
|
+
routing: routingSchema.optional(),
|
|
297
|
+
caps: capsSchema.optional(),
|
|
298
|
+
workerModel: z.unknown().optional(),
|
|
299
|
+
escalation: escalationSchema.optional(),
|
|
300
|
+
authority: authoritySchema.optional(),
|
|
301
|
+
releasePolicy: releasePolicySchema.optional(),
|
|
302
|
+
policy: projectPolicySchema.optional(),
|
|
303
|
+
reporting: reportingSchema.optional(),
|
|
304
|
+
// Roots silently fall back to the default when unusable, so the schema
|
|
305
|
+
// admits any shape and the normaliser picks the usable path.
|
|
306
|
+
workspaceRoot: z.unknown().optional(),
|
|
307
|
+
mirrorRoot: z.unknown().optional(),
|
|
308
|
+
})
|
|
309
|
+
.loose()
|
|
310
|
+
.describe("One product this conductor services");
|
|
311
|
+
|
|
312
|
+
const configSchema = z
|
|
313
|
+
.object({
|
|
314
|
+
$schema: z.string().optional().describe("Path to the shipped config.schema.json"),
|
|
315
|
+
version: z.number().describe(`The config format version (${READABLE_CONFIG_VERSIONS.join(" or ")})`),
|
|
316
|
+
defaults: capsSchema.optional(),
|
|
317
|
+
projects: z.array(projectSchema).min(1, `"projects" must be a non-empty array — the dispatcher has nothing to service otherwise`),
|
|
318
|
+
})
|
|
319
|
+
.loose()
|
|
320
|
+
.describe("On-disk configuration for omp-conductor");
|
|
321
|
+
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
// Emitted schema
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
|
|
326
|
+
/** The zod schema `loadConfig` parses the on-disk config with. */
|
|
327
|
+
export const ConfigSchema = configSchema;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* The draft 2020-12 JSON Schema rendering of {@link ConfigSchema}, shipped to
|
|
331
|
+
* `schema/config.schema.json` (see the `bun run schema` script) so editors can
|
|
332
|
+
* validate a hand-written config against the same vocabulary the loader enforces.
|
|
333
|
+
*/
|
|
334
|
+
export function configJsonSchema(): object {
|
|
335
|
+
return z.toJSONSchema(ConfigSchema) as object;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Re-exports the renderer consumes without duplicating the lists.
|
|
339
|
+
export {
|
|
340
|
+
REPORT_SCOPE_LIST,
|
|
341
|
+
INTERRUPT_CATEGORY_LIST,
|
|
342
|
+
WEEKDAY_LIST,
|
|
343
|
+
DIGEST_CADENCE_LIST,
|
|
344
|
+
AUTHORITY_HOLDER_LIST,
|
|
345
|
+
BASE_FRESHNESS_LIST,
|
|
346
|
+
DRAFT_POLICY_LIST,
|
|
347
|
+
BEHIND_BASE_ACTION_LIST,
|
|
348
|
+
RELEASE_REQUIREMENT_LIST,
|
|
349
|
+
RELEASE_SHAPE_LIST,
|
|
350
|
+
ORCHESTRATOR_MODE_LIST,
|
|
351
|
+
LEGACY_RELEASE_POLICY_LIST,
|
|
352
|
+
};
|