omp-conductor 0.3.18 → 0.3.20

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/src/config.ts CHANGED
@@ -20,14 +20,17 @@ import {
20
20
  CONFIG_VERSION,
21
21
  DEFAULT_AUTHORITY,
22
22
  DEFAULT_CAPS,
23
+ DEFAULT_RELEASE_POLICY,
23
24
  DEFAULT_REPORT_SCOPE,
24
25
  ORCHESTRATOR_MODES,
25
26
  READABLE_CONFIG_VERSIONS,
27
+ RELEASE_POLICIES,
26
28
  REPORT_SCOPES,
27
29
  type Caps,
28
30
  type ConductorConfig,
29
31
  type ProjectConfig,
30
32
  type ReportScope,
33
+ type ReleasePolicy,
31
34
  type RepoTarget,
32
35
  } from "./types.ts";
33
36
 
@@ -137,24 +140,28 @@ export function saveConfig(c: ConductorConfig): void {
137
140
  }
138
141
 
139
142
  /**
140
- * Layers a project's overrides on the global defaults, field by field, so a
141
- * project that pins one cap still inherits the other five. `??` not `||`: a
142
- * deliberate `dailySpendUsd: 0` is a hard stop, not "unset", and `null` is a
143
- * deliberate "no spend gate" that must not fall through to the default.
144
- * Spelled out per field so adding a `Caps` member fails to compile here.
143
+ * Layers a project's overrides on the global defaults, field by field. `??`
144
+ * not `||`: a deliberate `dailySpendUsd: 0` is a hard stop, and `null` is a
145
+ * deliberate "no spend gate". Spelled out so a new cap fails compilation here.
145
146
  */
146
147
  export function resolveCaps(p: ProjectConfig, defaults: Caps): Caps {
147
148
  const o: Partial<Caps> = p.caps ?? {};
148
149
  return {
149
150
  maxConcurrentWorkers: o.maxConcurrentWorkers ?? defaults.maxConcurrentWorkers,
150
- // nullish only — `null` means off; do not coalesce it to the default.
151
151
  dailySpendUsd: o.dailySpendUsd !== undefined ? o.dailySpendUsd : defaults.dailySpendUsd,
152
152
  workerMaxTurns: o.workerMaxTurns ?? defaults.workerMaxTurns,
153
153
  workerWallClockMs: o.workerWallClockMs ?? defaults.workerWallClockMs,
154
154
  maxAttemptsPerIssue: o.maxAttemptsPerIssue ?? defaults.maxAttemptsPerIssue,
155
+ maxContinuationsPerIssue:
156
+ o.maxContinuationsPerIssue ?? defaults.maxContinuationsPerIssue,
155
157
  };
156
158
  }
157
159
 
160
+ /** Old configs and omitted keys are fail-closed at the enforcement boundary. */
161
+ export function resolveReleasePolicy(p: ProjectConfig): ReleasePolicy {
162
+ return p.releasePolicy ?? DEFAULT_RELEASE_POLICY;
163
+ }
164
+
158
165
  /**
159
166
  * Resolves a project by name, or the only project when the name is omitted.
160
167
  * Refuses to guess between several: picking one silently would spend the wrong
@@ -275,6 +282,14 @@ function normalizeProject(
275
282
 
276
283
  const escalation = normalizeEscalation(raw["escalation"], label, problems);
277
284
  const authority = normalizeAuthority(raw["authority"], label, problems);
285
+ const releasePolicy = pickLiteral(
286
+ raw["releasePolicy"],
287
+ RELEASE_POLICIES,
288
+ DEFAULT_RELEASE_POLICY,
289
+ `${label}: releasePolicy`,
290
+ RELEASE_POLICIES.map((value) => JSON.stringify(value)).join(" or "),
291
+ problems,
292
+ );
278
293
 
279
294
  const caps = coerceCaps(raw["caps"], `${label}: caps`, problems, legacyCaps);
280
295
  const reporting = normalizeReporting(raw["reporting"], label, problems);
@@ -300,6 +315,7 @@ function normalizeProject(
300
315
  ...(workerModel === undefined ? {} : { workerModel }),
301
316
  escalation,
302
317
  authority,
318
+ releasePolicy,
303
319
  reporting,
304
320
  workspaceRoot: expandHome(pickString(raw["workspaceRoot"], join(stateDir(), "worktrees"))),
305
321
  mirrorRoot: expandHome(pickString(raw["mirrorRoot"], join(stateDir(), "mirrors"))),