@bridge_gpt/mcp-server 0.2.67 → 0.2.68
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 +178 -15
- package/build/agent-launchers/claude-executor-adapter.js +27 -0
- package/build/agents.generated.js +22 -2
- package/build/bitbucket-workspace-remote.js +136 -0
- package/build/bootstrap-project-identity.js +24 -0
- package/build/bridge-permissions.js +829 -0
- package/build/claude-settings.js +18 -0
- package/build/cli-help.js +21 -0
- package/build/command-assets-doctor.js +24 -70
- package/build/command-provisioning.js +29 -5
- package/build/commands.generated.js +8 -8
- package/build/conductor/bridge-api-client.js +714 -0
- package/build/conductor/command-drift-production.js +92 -0
- package/build/conductor/command-drift.js +89 -0
- package/build/conductor/doctor.js +69 -26
- package/build/conductor/epic-branch-ci-inspection.js +1375 -0
- package/build/conductor/epic-implementer-cli.js +76 -22
- package/build/conductor/gate-discovery.js +890 -0
- package/build/conductor/install-doctor.js +15 -3
- package/build/conductor/json-output.js +63 -0
- package/build/conductor/readiness-cli.js +95 -10
- package/build/conductor/readiness-sections.js +369 -3
- package/build/conductor/readiness.js +211 -8
- package/build/conductor/recovery-cli.js +110 -4
- package/build/conductor/recovery-operations.js +99 -1
- package/build/connect-bitbucket-api.js +41 -6
- package/build/connect-bitbucket.js +106 -33
- package/build/credential-store.js +207 -47
- package/build/docs.generated.js +57 -3
- package/build/doctor.js +92 -1
- package/build/drive-epic.js +45 -8
- package/build/epic-coherence-input.test-fixtures.js +4 -3
- package/build/epic-implementer-bin.js +1374 -25
- package/build/epic-workarounds-path.js +39 -0
- package/build/executor/cli.js +30 -0
- package/build/executor/completion-contract.js +18 -5
- package/build/executor/deps.js +4 -1
- package/build/executor/gate-qualification-job.js +499 -0
- package/build/executor/guarded-fast-forward-push.js +129 -0
- package/build/executor/integration-job.js +122 -8
- package/build/executor/job-errors.js +8 -1
- package/build/executor/job-runner.js +589 -47
- package/build/executor/likely-tests.js +290 -0
- package/build/executor/live-worker-registry.js +1 -1
- package/build/executor/observation.js +31 -2
- package/build/executor/packaged-recipe.js +106 -0
- package/build/executor/permissions.js +115 -28
- package/build/executor/platform.js +1 -1
- package/build/executor/preflight.js +61 -12
- package/build/executor/process-containment.js +36 -12
- package/build/executor/process.js +15 -7
- package/build/executor/prompt-spec.js +33 -1
- package/build/executor/recently-failed-tests-file.js +71 -0
- package/build/executor/reconcile-overlap-job.js +41 -23
- package/build/executor/required-verification.js +1 -1
- package/build/executor/required-work-ledger.js +105 -17
- package/build/executor/retirement.js +69 -0
- package/build/executor/runner.js +114 -11
- package/build/executor/test-clock.js +15 -0
- package/build/executor/types.js +32 -0
- package/build/executor/viewer-tabs.js +58 -8
- package/build/executor/worker-command.js +23 -7
- package/build/executor/worker-diagnostics.js +37 -0
- package/build/executor/worker-finalization.js +31 -5
- package/build/executor/worker-guard-hook.js +179 -25
- package/build/executor/worker-log.js +4 -3
- package/build/executor/worker-verification-guidance.js +37 -0
- package/build/executor/worktree.js +88 -11
- package/build/first-build-advisory.js +97 -0
- package/build/generated-assets-advisory.js +14 -0
- package/build/generated-assets-catalog.js +66 -0
- package/build/generated-assets-context.js +46 -0
- package/build/generated-assets-dependencies.js +26 -0
- package/build/generated-assets-doctor.js +50 -0
- package/build/generated-assets-health.js +65 -0
- package/build/generated-assets-host.js +17 -0
- package/build/generated-assets-inventory.js +17 -0
- package/build/generated-assets-paths.js +60 -0
- package/build/generated-assets-publisher.js +133 -0
- package/build/generated-assets-remedy.js +25 -0
- package/build/generated-assets-repair.js +72 -0
- package/build/generated-assets-session.js +71 -0
- package/build/generated-assets-test-support.js +35 -0
- package/build/index.js +964 -768
- package/build/init.js +54 -177
- package/build/install-bridge-conductor-gate.js +1372 -0
- package/build/install-bridge-conductor.js +289 -36
- package/build/install-bridge.js +544 -37
- package/build/mcp-install-state.js +50 -4
- package/build/merge-workflow-install.js +3 -4
- package/build/pipeline-utils.js +6 -2
- package/build/pipelines.generated.js +22 -142
- package/build/plan-execution-ledger.js +2 -32
- package/build/plane/manifest.js +16 -1
- package/build/plane/preflight.js +54 -1
- package/build/printed-remedy-commands.js +16 -1
- package/build/readme.generated.js +1 -1
- package/build/repair-assets.js +93 -0
- package/build/run-unit-tests-launcher.js +130 -49
- package/build/secret-redaction.js +33 -0
- package/build/setup-epic.js +108 -3
- package/build/start-tickets-prereqs.js +70 -16
- package/build/start-tickets.js +29 -5
- package/build/tool-surface-gating.js +36 -0
- package/build/upgrade-cli.js +55 -1
- package/build/version.generated.js +3 -3
- package/build/worker-guard-hook-bin.js +3 -2
- package/build/worker-guard-hook-main.js +1 -1
- package/build/worktree-core.js +15 -2
- package/docs/EPIC-IMPLEMENTER-INSTALL.md +30 -12
- package/docs/EPIC-IMPLEMENTER.md +68 -2
- package/package.json +3 -3
- package/pipelines/greenfield-setup.json +4 -128
- package/pipelines/implement-ticket.json +3 -2
|
@@ -106,7 +106,8 @@
|
|
|
106
106
|
* The command does NOT persist a repository-level review policy: that is
|
|
107
107
|
* BAPI-694's. The policy selected here is per-run and is reported as such.
|
|
108
108
|
*/
|
|
109
|
-
import { PRINTED_INSTALL_COMMAND } from "./printed-remedy-commands.js";
|
|
109
|
+
import { PRINTED_INSTALL_COMMAND, PRINTED_QUALIFY_GATE_COMMAND } from "./printed-remedy-commands.js";
|
|
110
|
+
import { emptyConductorGateFlags, hasExplicitGateInput, parseLocalCommandFlag, parseEpicBranchCiDecisionFlag, parseMigrationGuardFlag, resolveConductorGate, runEpicBranchCiInspection, runClearConductorGate, runConductorGateQualification, runQualifyConductorGate, storeConductorGate, validateConductorGateFlags, } from "./install-bridge-conductor-gate.js";
|
|
110
111
|
import { bootstrapConductorSupervisorDefaults, classifySupervisorBootstrapFailure, fetchConductorReadiness, previewConductorSupervisorBootstrap, } from "./conductor/bridge-api-client.js";
|
|
111
112
|
import { CONDUCTOR_OPERATOR_RUNBOOK_POINTER, EXECUTOR_PROVISIONING_GUIDANCE, conductorInstallDoctorExitCode, conductorInstallDoctorHasFatal, formatConductorInstallDoctorReport, } from "./conductor/install-doctor.js";
|
|
112
113
|
import { describeNativeLedgerAvailability, } from "./conductor/doctor.js";
|
|
@@ -131,9 +132,18 @@ export const CONDUCTOR_REVIEW_POLICY_SOURCES = [
|
|
|
131
132
|
"verdict_protocol",
|
|
132
133
|
"native_review_decision",
|
|
133
134
|
"none",
|
|
135
|
+
"repository_policy",
|
|
134
136
|
];
|
|
135
|
-
/**
|
|
136
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Install-time review policy when the operator does not select one (BAPI-1406).
|
|
139
|
+
*
|
|
140
|
+
* `repository_policy` needs no Claude-review workflow, so an omitted flag
|
|
141
|
+
* scaffolds none. It is an install-time assumption ONLY: nothing persists it.
|
|
142
|
+
* {@link InstallBridgeConductorOptions.reviewPolicyExplicit} stays the sole basis
|
|
143
|
+
* for storing a repository default, and the server stamps the run-level default
|
|
144
|
+
* at first approval. AI review is an explicit `--review-policy verdict_protocol`.
|
|
145
|
+
*/
|
|
146
|
+
export const DEFAULT_CONDUCTOR_REVIEW_POLICY = "repository_policy";
|
|
137
147
|
/** Usage text for `install-bridge conductor`. */
|
|
138
148
|
export function getInstallBridgeConductorUsage() {
|
|
139
149
|
return [
|
|
@@ -145,14 +155,53 @@ export function getInstallBridgeConductorUsage() {
|
|
|
145
155
|
"",
|
|
146
156
|
"Options:",
|
|
147
157
|
" --repo <name> Repository name (default: BAPI_REPO_NAME / .bridge/config)",
|
|
148
|
-
" --required-check <name>
|
|
158
|
+
" --required-check <name> A provider check for the gate (repeatable). Explicit",
|
|
159
|
+
" owner input: stored as a confirmed provider gate and",
|
|
160
|
+
" projected into the done gate, as before.",
|
|
149
161
|
" --required-checks a,b Comma-separated form of --required-check",
|
|
162
|
+
"",
|
|
163
|
+
"Repository gate (BAPI-1391) — what must pass before a child merges. A CONFIRMED",
|
|
164
|
+
"gate is required: provider checks, local commands, both (mixed), or none.",
|
|
165
|
+
"Without gate flags, a confirmed gate already stored (including an older",
|
|
166
|
+
"--required-check setup) is used unchanged; otherwise discovery reads this",
|
|
167
|
+
"working tree and the provider's real check history and PROPOSES a gate, which",
|
|
168
|
+
"is stored only after you confirm or edit it at a prompt. Discovery saves",
|
|
169
|
+
"nothing. Explicit gate flags are owner input and are stored as confirmed.",
|
|
170
|
+
" --provider-check <name> An exact provider-reported check name (repeatable).",
|
|
171
|
+
" Admission refuses one that has never reported.",
|
|
172
|
+
" --local-command <cmd> A local command the executor runs before each merge",
|
|
173
|
+
" (repeatable): a discovered candidate id such as",
|
|
174
|
+
" npm:test, or <name>=<plain words> such as",
|
|
175
|
+
" unit=pytest -q (no shell syntax; run without a shell).",
|
|
176
|
+
" --migration-guard <ref> provider:<check> or local:<command>: the member that",
|
|
177
|
+
" guards Alembic migrations.",
|
|
178
|
+
" --gate-none No gate: unattended runs need approval for every",
|
|
179
|
+
" child merge. Exclusive with the member flags.",
|
|
180
|
+
" --customized-command <n> A packaged command this repository customizes, stored",
|
|
181
|
+
" in the confirmed gate (repeatable), e.g. implement-ticket.",
|
|
182
|
+
" --confirm-gate Confirm explicit gate flags even when they REMOVE",
|
|
183
|
+
" members from the stored gate (otherwise you are asked).",
|
|
184
|
+
" --clear-gate Clear the confirmed gate (its own consent), then exit.",
|
|
185
|
+
" --qualify-gate Qualify a confirmed local or mixed gate: queue the",
|
|
186
|
+
" qualification job and drain it with a short-lived",
|
|
187
|
+
" repository-wide executor limited to that job type.",
|
|
188
|
+
" Alone, it does only that; with gate flags it runs",
|
|
189
|
+
" after they are stored, without the offer prompt.",
|
|
190
|
+
" Every gate write first checks that the server advertises gate_profile.v1;",
|
|
191
|
+
" against an older server it prints \"Bridge API update required\" and exits 1.",
|
|
192
|
+
"",
|
|
150
193
|
` --review-policy <source> One of: ${CONDUCTOR_REVIEW_POLICY_SOURCES.join(", ")}`,
|
|
151
|
-
|
|
152
|
-
"
|
|
153
|
-
"
|
|
154
|
-
"
|
|
155
|
-
" stores no default at all.",
|
|
194
|
+
" Omitted, install assumes repository_policy: the",
|
|
195
|
+
" prospective default for a new feature-branch run at",
|
|
196
|
+
" first approval when neither the run nor the",
|
|
197
|
+
" repository sets a policy. It scaffolds no Claude",
|
|
198
|
+
" review workflow and stores no default at all.",
|
|
199
|
+
" AI review is an explicit opt-in: pass",
|
|
200
|
+
" --review-policy verdict_protocol to scaffold the",
|
|
201
|
+
" workflow. Supplying any value EXPLICITLY also",
|
|
202
|
+
" persists it as the supervisor project default,",
|
|
203
|
+
" stamped into a new run that carries no review",
|
|
204
|
+
" policy of its own.",
|
|
156
205
|
" --epic-key <KEY> Epic key for the setup-epic verification phase",
|
|
157
206
|
" --plan-file <path> Plan sidecar for the setup-epic verification phase",
|
|
158
207
|
" --executor-id <id> Stable executor id. Supplying it OPTS IN to the",
|
|
@@ -196,6 +245,11 @@ export function getInstallBridgeConductorUsage() {
|
|
|
196
245
|
" start-consent prompt.",
|
|
197
246
|
" Still asks the base-URL confirmation below first —",
|
|
198
247
|
" its doctor readiness GET carries the repo API key.",
|
|
248
|
+
" --epic-branch-ci-decision <include|decline>",
|
|
249
|
+
" (BAPI-1405, GitHub only) Record whether child merges",
|
|
250
|
+
" into an epic branch wait for CI on that PR. Without",
|
|
251
|
+
" it you are asked; a non-interactive run records",
|
|
252
|
+
" nothing. Never consent to change an approval rule.",
|
|
199
253
|
" -h, --help Show this help",
|
|
200
254
|
"",
|
|
201
255
|
"The resolved BAPI_BASE_URL is displayed and confirmed BEFORE any network call",
|
|
@@ -283,10 +337,12 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
283
337
|
let secretName;
|
|
284
338
|
let branches;
|
|
285
339
|
const requiredChecks = [];
|
|
340
|
+
const gate = emptyConductorGateFlags();
|
|
286
341
|
let launchProfile;
|
|
287
342
|
let baseUrl;
|
|
288
343
|
let executionMode;
|
|
289
344
|
let policyFile;
|
|
345
|
+
let epicBranchCiDecision;
|
|
290
346
|
const takeValue = (arg, inline, next) => {
|
|
291
347
|
if (inline !== undefined) {
|
|
292
348
|
if (inline.trim().length === 0)
|
|
@@ -324,6 +380,21 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
324
380
|
return { status: "error", message: "--repo-wide takes no value" };
|
|
325
381
|
repoWide = true;
|
|
326
382
|
break;
|
|
383
|
+
case "--gate-none":
|
|
384
|
+
case "--confirm-gate":
|
|
385
|
+
case "--clear-gate":
|
|
386
|
+
case "--qualify-gate":
|
|
387
|
+
if (inline !== undefined)
|
|
388
|
+
return { status: "error", message: `${flag} takes no value` };
|
|
389
|
+
if (flag === "--gate-none")
|
|
390
|
+
gate.gateNone = true;
|
|
391
|
+
else if (flag === "--confirm-gate")
|
|
392
|
+
gate.confirmGate = true;
|
|
393
|
+
else if (flag === "--clear-gate")
|
|
394
|
+
gate.clearGate = true;
|
|
395
|
+
else
|
|
396
|
+
gate.qualifyGate = true;
|
|
397
|
+
break;
|
|
327
398
|
case "--repo":
|
|
328
399
|
case "--required-check":
|
|
329
400
|
case "--required-checks":
|
|
@@ -338,7 +409,12 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
338
409
|
case "--launch-profile":
|
|
339
410
|
case "--base-url":
|
|
340
411
|
case "--execution-mode":
|
|
341
|
-
case "--policy-file":
|
|
412
|
+
case "--policy-file":
|
|
413
|
+
case "--provider-check":
|
|
414
|
+
case "--local-command":
|
|
415
|
+
case "--migration-guard":
|
|
416
|
+
case "--customized-command":
|
|
417
|
+
case "--epic-branch-ci-decision": {
|
|
342
418
|
const taken = takeValue(flag, inline, argv[i + 1]);
|
|
343
419
|
if (!taken.ok)
|
|
344
420
|
return { status: "error", message: taken.error };
|
|
@@ -347,8 +423,35 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
347
423
|
const value = taken.value;
|
|
348
424
|
if (flag === "--repo")
|
|
349
425
|
repo = value;
|
|
350
|
-
else if (flag === "--required-check")
|
|
426
|
+
else if (flag === "--required-check" || flag === "--provider-check")
|
|
351
427
|
requiredChecks.push(value);
|
|
428
|
+
else if (flag === "--local-command") {
|
|
429
|
+
const local = parseLocalCommandFlag(value);
|
|
430
|
+
if (!local.ok)
|
|
431
|
+
return { status: "error", message: local.error };
|
|
432
|
+
gate.localCommands.push(local.value);
|
|
433
|
+
}
|
|
434
|
+
else if (flag === "--migration-guard") {
|
|
435
|
+
if (gate.migrationGuard !== undefined) {
|
|
436
|
+
return { status: "error", message: "--migration-guard may be given once" };
|
|
437
|
+
}
|
|
438
|
+
const guard = parseMigrationGuardFlag(value);
|
|
439
|
+
if (!guard.ok)
|
|
440
|
+
return { status: "error", message: guard.error };
|
|
441
|
+
gate.migrationGuard = guard.value;
|
|
442
|
+
}
|
|
443
|
+
else if (flag === "--epic-branch-ci-decision") {
|
|
444
|
+
if (epicBranchCiDecision !== undefined) {
|
|
445
|
+
return { status: "error", message: "--epic-branch-ci-decision may be given once" };
|
|
446
|
+
}
|
|
447
|
+
const decision = parseEpicBranchCiDecisionFlag(value);
|
|
448
|
+
if (!decision.ok)
|
|
449
|
+
return { status: "error", message: decision.error };
|
|
450
|
+
epicBranchCiDecision = decision.value;
|
|
451
|
+
}
|
|
452
|
+
else if (flag === "--customized-command") {
|
|
453
|
+
gate.customizedCommands = [...(gate.customizedCommands ?? []), value];
|
|
454
|
+
}
|
|
352
455
|
else if (flag === "--required-checks")
|
|
353
456
|
requiredChecks.push(...splitList(value));
|
|
354
457
|
else if (flag === "--epic-key")
|
|
@@ -407,9 +510,12 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
407
510
|
return { status: "error", message: `Unknown option: ${flag}` };
|
|
408
511
|
}
|
|
409
512
|
}
|
|
410
|
-
// Normalize + validate the
|
|
411
|
-
// never reach
|
|
412
|
-
//
|
|
513
|
+
// Normalize + validate the provider-check list at parse time so a bad list can
|
|
514
|
+
// never reach a request. BAPI-1391 replaced "at least one --required-check" with
|
|
515
|
+
// "a confirmed gate": a stored gate, a confirmed proposal, or explicit gate
|
|
516
|
+
// flags — and a confirmed local-only or `none` gate is valid. An empty CI list
|
|
517
|
+
// is still never written as a passing gate: the server accepts one only for a
|
|
518
|
+
// stored local or none gate, and the run stops without a confirmed gate.
|
|
413
519
|
const normalizedChecks = [];
|
|
414
520
|
const seen = new Set();
|
|
415
521
|
for (const check of requiredChecks) {
|
|
@@ -419,13 +525,10 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
419
525
|
seen.add(check);
|
|
420
526
|
normalizedChecks.push(check);
|
|
421
527
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
"so the bootstrap refuses to write one.",
|
|
427
|
-
};
|
|
428
|
-
}
|
|
528
|
+
gate.providerChecks = normalizedChecks;
|
|
529
|
+
const gateError = validateConductorGateFlags(gate, { replaceDoneGate });
|
|
530
|
+
if (gateError !== null)
|
|
531
|
+
return { status: "error", message: gateError };
|
|
429
532
|
// BAPI-1137 — the claim scope is validated HERE, at parse time, for the same
|
|
430
533
|
// reason the required-check list is: a unit that cannot start is worse than a
|
|
431
534
|
// refused invocation, and discovering it costs a reboot to notice.
|
|
@@ -487,6 +590,7 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
487
590
|
repo,
|
|
488
591
|
dryRun,
|
|
489
592
|
requiredChecks: normalizedChecks,
|
|
593
|
+
gate,
|
|
490
594
|
reviewPolicy,
|
|
491
595
|
reviewPolicyExplicit,
|
|
492
596
|
epicKey,
|
|
@@ -505,6 +609,7 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
505
609
|
? { executionMode: executionMode ?? (executorId !== undefined ? "durable" : "local") }
|
|
506
610
|
: {}),
|
|
507
611
|
...(policyFile ? { policyFile } : {}),
|
|
612
|
+
...(epicBranchCiDecision ? { epicBranchCiDecision } : {}),
|
|
508
613
|
},
|
|
509
614
|
};
|
|
510
615
|
}
|
|
@@ -690,12 +795,21 @@ async function confirm(deps, promptText) {
|
|
|
690
795
|
}
|
|
691
796
|
function renderCapabilityMatrix(deps, readiness) {
|
|
692
797
|
const apiReady = readiness !== null;
|
|
798
|
+
// BAPI-1391 — a CONFIRMED local-only or `none` gate has no CI checks by
|
|
799
|
+
// design: its gate is the profile (executor-run commands, or approval for
|
|
800
|
+
// every merge), so an empty CI list is not "unconfigured" there. Read from
|
|
801
|
+
// the server's own confirmed-profile state; an older server reports no
|
|
802
|
+
// `gate_profile` block and keeps the original rule exactly.
|
|
803
|
+
const gateProfile = readiness?.gate_profile ?? null;
|
|
804
|
+
const confirmedCiFreeGate = gateProfile !== null &&
|
|
805
|
+
gateProfile.confirmed === true &&
|
|
806
|
+
(gateProfile.kind === "local" || gateProfile.kind === "none");
|
|
693
807
|
// Derived from READINESS, never from the bootstrap's HTTP status: a 200 proves
|
|
694
808
|
// the write was accepted, not that the resulting posture is safe.
|
|
695
809
|
const configurationReady = readiness !== null &&
|
|
696
810
|
readiness.supervisor.setup_present &&
|
|
697
811
|
readiness.supervisor.config_present &&
|
|
698
|
-
!readiness.supervisor.required_checks_empty;
|
|
812
|
+
(!readiness.supervisor.required_checks_empty || confirmedCiFreeGate);
|
|
699
813
|
// `ready === null` (never provisioned) and `false` (stale) are both "not ready"
|
|
700
814
|
// here, but only a recent server-observed claim/heartbeat counts as ready — a
|
|
701
815
|
// configured command or a local process does not.
|
|
@@ -706,6 +820,14 @@ function renderCapabilityMatrix(deps, readiness) {
|
|
|
706
820
|
deps.log(` api_ready: ${apiReady}`);
|
|
707
821
|
deps.log(` configuration_ready: ${configurationReady}`);
|
|
708
822
|
deps.log(` executor_ready: ${executorReady}`);
|
|
823
|
+
if (gateProfile !== null) {
|
|
824
|
+
// Names nothing: the kind, provenance, revision, and qualification state only.
|
|
825
|
+
deps.log(` repository gate: ${gateProfile.confirmed ? (gateProfile.kind ?? "unknown") : "not confirmed"}` +
|
|
826
|
+
(gateProfile.confirmed ? ` (${gateProfile.source}, revision ${gateProfile.revision}` +
|
|
827
|
+
(gateProfile.qualification_state && gateProfile.qualification_state !== "not_required"
|
|
828
|
+
? `, local gate ${gateProfile.qualification_state})`
|
|
829
|
+
: ")") : ""));
|
|
830
|
+
}
|
|
709
831
|
deps.log(` executor provisioning: ${EXECUTOR_PROVISIONING_GUIDANCE}`);
|
|
710
832
|
deps.log(` runbook: ${CONDUCTOR_OPERATOR_RUNBOOK_POINTER}`);
|
|
711
833
|
return { apiReady, configurationReady, executorReady };
|
|
@@ -1505,6 +1627,27 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1505
1627
|
const accessResult = await deps.resolveAccess(options.repo, baseUrl);
|
|
1506
1628
|
const access = accessResult.ok ? accessResult.access : null;
|
|
1507
1629
|
const accessError = accessResult.ok ? undefined : accessResult.error;
|
|
1630
|
+
// ---- BAPI-1391: the two standalone gate actions -----------------------
|
|
1631
|
+
// `--clear-gate`, and `--qualify-gate` without gate flags, act on the stored
|
|
1632
|
+
// gate and stop: they never bootstrap, scaffold, or provision anything else.
|
|
1633
|
+
const gateIo = {
|
|
1634
|
+
log: deps.log,
|
|
1635
|
+
errorLog: deps.errorLog,
|
|
1636
|
+
promptLine: deps.promptLine,
|
|
1637
|
+
fetchJson: deps.fetchJson,
|
|
1638
|
+
postJson: deps.postJson,
|
|
1639
|
+
};
|
|
1640
|
+
const qualifyOnly = options.gate.qualifyGate && !hasExplicitGateInput(options.gate);
|
|
1641
|
+
if (options.gate.clearGate || qualifyOnly) {
|
|
1642
|
+
if (!access) {
|
|
1643
|
+
deps.errorLog(`Bridge API access could not be resolved: ${accessError ?? "unknown"}`);
|
|
1644
|
+
deps.errorLog("Nothing was written.");
|
|
1645
|
+
return 1;
|
|
1646
|
+
}
|
|
1647
|
+
return options.gate.clearGate
|
|
1648
|
+
? runClearConductorGate(gateIo, access, { dryRun: options.dryRun })
|
|
1649
|
+
: runQualifyConductorGate(gateIo, access, deps.gate, { dryRun: options.dryRun, baseUrl });
|
|
1650
|
+
}
|
|
1508
1651
|
// ---- Phase 1: unified doctor, BEFORE every write -----------------------
|
|
1509
1652
|
deps.log("Phase 1/8 — running the unified epic-implementer doctor (read-only)…");
|
|
1510
1653
|
const firstReport = await deps.runDoctor({
|
|
@@ -1527,8 +1670,10 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1527
1670
|
"default and stamped into a new run that sets no review policy of its own. " +
|
|
1528
1671
|
"An explicit per-run policy always wins, and changing this later never " +
|
|
1529
1672
|
"alters an already-approved run."
|
|
1530
|
-
: "
|
|
1531
|
-
"
|
|
1673
|
+
: " Install assumption only: no repository default is persisted and no " +
|
|
1674
|
+
"Claude review workflow is scaffolded. A new feature-branch run receives " +
|
|
1675
|
+
"repository_policy at first approval when nothing else sets one — pass " +
|
|
1676
|
+
"--review-policy verdict_protocol to opt in to AI review.");
|
|
1532
1677
|
// ---- Executor preflight: AFTER the URL consent, BEFORE any mutation ----
|
|
1533
1678
|
// Placed here so an unsupported host or a missing `wt`/`git`/`claude` refuses
|
|
1534
1679
|
// before the bootstrap POST, the workflow write, or a single filesystem
|
|
@@ -1577,15 +1722,52 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1577
1722
|
deps.log(" executor provisioning skipped — pass --executor-id <id> to write and start a " +
|
|
1578
1723
|
"service unit for this host.");
|
|
1579
1724
|
}
|
|
1725
|
+
// ---- BAPI-1391: the repository gate, decided BEFORE any write ----------
|
|
1726
|
+
// Capability probe first (a read, after the base-URL consent), then the stored
|
|
1727
|
+
// gate, explicit gate flags, or a discovery proposal the owner confirms. Under
|
|
1728
|
+
// --dry-run it only shows the decision: no PUT, no enqueue, no gate prompt.
|
|
1729
|
+
if (!access) {
|
|
1730
|
+
deps.errorLog("Bridge API access could not be resolved; aborting before any write.");
|
|
1731
|
+
return 1;
|
|
1732
|
+
}
|
|
1733
|
+
const gatePhase = await resolveConductorGate(gateIo, access, options.gate, deps.gate, {
|
|
1734
|
+
dryRun: options.dryRun,
|
|
1735
|
+
});
|
|
1736
|
+
if (gatePhase.kind === "exit")
|
|
1737
|
+
return gatePhase.code;
|
|
1738
|
+
const gatePlan = gatePhase.kind === "plan" ? gatePhase.plan : null;
|
|
1739
|
+
// The bootstrap's CI list is the gate's provider leg. A local-only or `none`
|
|
1740
|
+
// gate sends an empty list, which the server accepts only for a stored gate
|
|
1741
|
+
// of that kind.
|
|
1742
|
+
const bootstrapChecks = gatePlan?.desired.providerChecks ?? options.requiredChecks;
|
|
1580
1743
|
// ---- Dry run: preview and RETURN before every write/prompt -------------
|
|
1581
1744
|
if (options.dryRun) {
|
|
1582
1745
|
deps.log("");
|
|
1583
1746
|
deps.log("Dry run — no writes will be performed.");
|
|
1747
|
+
if (gatePlan === null) {
|
|
1748
|
+
deps.log(" Repository gate: none confirmed — a real run asks you to confirm one before any write.");
|
|
1749
|
+
}
|
|
1750
|
+
else if (gatePlan.write !== null) {
|
|
1751
|
+
// Field NAMES and counts only, as below.
|
|
1752
|
+
deps.log(` Would PUT /jira/epic-runs/supervisor-setup/gate-profile/ (confirm, expected revision ` +
|
|
1753
|
+
`${gatePlan.write.expected_revision}) with field NAMES: kind, ` +
|
|
1754
|
+
`provider_checks (${gatePlan.desired.providerChecks.length}), ` +
|
|
1755
|
+
`local_commands (${gatePlan.desired.localCommands.length}), migration_guard` +
|
|
1756
|
+
(gatePlan.write.customized_commands !== undefined ? ", customized_commands" : ""));
|
|
1757
|
+
}
|
|
1758
|
+
else {
|
|
1759
|
+
deps.log(" Repository gate: already confirmed as shown — no gate write.");
|
|
1760
|
+
}
|
|
1761
|
+
if (gatePlan !== null && gatePlan.desired.localCommands.length > 0) {
|
|
1762
|
+
deps.log(" Gate qualification: would be offered after the gate is stored — no job was queued.");
|
|
1763
|
+
}
|
|
1764
|
+
deps.log(" Epic-branch CI: a real run inspects workflow triggers and epic-branch rules (GitHub only), " +
|
|
1765
|
+
"then asks include or decline — nothing was inspected or recorded.");
|
|
1584
1766
|
deps.log(" Would PREVIEW, then POST /jira/epic-runs/supervisor-bootstrap with field NAMES:");
|
|
1585
1767
|
// Field NAMES and a COUNT only — never the check values, which are
|
|
1586
1768
|
// configuration content, and never the API key.
|
|
1587
1769
|
deps.log(" - done_gate_config.required_checks " +
|
|
1588
|
-
`(${
|
|
1770
|
+
`(${bootstrapChecks.length} check(s))`);
|
|
1589
1771
|
if (options.reviewPolicyExplicit)
|
|
1590
1772
|
deps.log(" - review_policy");
|
|
1591
1773
|
if (options.replaceDoneGate)
|
|
@@ -1636,10 +1818,10 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1636
1818
|
deps.log("complete-with-manual-steps (dry run)");
|
|
1637
1819
|
return 0;
|
|
1638
1820
|
}
|
|
1639
|
-
if (
|
|
1640
|
-
// Unreachable
|
|
1641
|
-
//
|
|
1642
|
-
deps.errorLog("
|
|
1821
|
+
if (gatePlan === null) {
|
|
1822
|
+
// Unreachable outside a dry run: the gate phase either decided a gate or
|
|
1823
|
+
// returned. Narrowed here so the bootstrap below always has one.
|
|
1824
|
+
deps.errorLog("No confirmed repository gate; aborting before any write.");
|
|
1643
1825
|
return 1;
|
|
1644
1826
|
}
|
|
1645
1827
|
// ---- Phase 2: bootstrap — preview, consent, exact approval, apply -------
|
|
@@ -1650,7 +1832,7 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1650
1832
|
deps.log("");
|
|
1651
1833
|
deps.log("Phase 2/8 — bootstrapping supervisor project defaults…");
|
|
1652
1834
|
const bootstrapRequest = {
|
|
1653
|
-
required_checks:
|
|
1835
|
+
required_checks: bootstrapChecks,
|
|
1654
1836
|
// BAPI-694 — the narrow ReviewPolicy object, sent ONLY on an explicit
|
|
1655
1837
|
// selection. `review_source` is deliberately NOT sent: on an existing
|
|
1656
1838
|
// repository an omitted field keeps its stored value, and a new repository
|
|
@@ -1661,6 +1843,32 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1661
1843
|
merge_approval_required: "true",
|
|
1662
1844
|
...(options.replaceDoneGate ? { replace_done_gate: true } : {}),
|
|
1663
1845
|
};
|
|
1846
|
+
// BAPI-1391 — the gate is stored FIRST, under its own combined consent, in
|
|
1847
|
+
// two cases:
|
|
1848
|
+
// * a gate with no provider checks (local-only or `none`): the server
|
|
1849
|
+
// accepts an empty CI list only for a stored gate of that kind;
|
|
1850
|
+
// * any change to an already STORED gate: the bootstrap apply would
|
|
1851
|
+
// otherwise run BAPI-1390's profile sync first, which merges the additive
|
|
1852
|
+
// CI list into the stored profile under a new revision — so a later PUT
|
|
1853
|
+
// would be refused as stale while the gate had silently been widened.
|
|
1854
|
+
// Stored first, the CI checks already equal the gate, the bootstrap adds
|
|
1855
|
+
// nothing, and the sync has nothing to change.
|
|
1856
|
+
// A legacy or unconfirmed repository keeps the original order (preview,
|
|
1857
|
+
// consent, apply) and is stored after the apply; the sync never touches it.
|
|
1858
|
+
const gateFirst = gatePlan.write !== null &&
|
|
1859
|
+
(bootstrapChecks.length === 0 || gatePlan.current.source === "stored");
|
|
1860
|
+
let storedGate = gatePlan.write === null ? gatePlan.current : null;
|
|
1861
|
+
if (gateFirst) {
|
|
1862
|
+
deps.log("");
|
|
1863
|
+
const gateOk = await confirm(deps, "Store this repository gate and write the safe project-default supervisor posture now? [y/N]: ");
|
|
1864
|
+
if (!gateOk) {
|
|
1865
|
+
deps.log("Aborted — no server write was attempted.");
|
|
1866
|
+
return 0;
|
|
1867
|
+
}
|
|
1868
|
+
storedGate = await storeConductorGate(gateIo, access, gatePlan);
|
|
1869
|
+
if (storedGate === null)
|
|
1870
|
+
return 1;
|
|
1871
|
+
}
|
|
1664
1872
|
let preview;
|
|
1665
1873
|
try {
|
|
1666
1874
|
preview = await previewConductorSupervisorBootstrap(access, bootstrapRequest, deps.postJson);
|
|
@@ -1673,11 +1881,14 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1673
1881
|
// ---- Consent #2: the bootstrap write itself ----------------------------
|
|
1674
1882
|
// Deliberately separate from the base-URL consent above (which was given
|
|
1675
1883
|
// before any network call). Confirming WHERE to write is not consent TO write.
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
deps
|
|
1680
|
-
|
|
1884
|
+
// A gate stored first above was stored under its own combined consent.
|
|
1885
|
+
if (!gateFirst) {
|
|
1886
|
+
deps.log("");
|
|
1887
|
+
const bootstrapOk = await confirm(deps, "Write the safe project-default supervisor posture now? [y/N]: ");
|
|
1888
|
+
if (!bootstrapOk) {
|
|
1889
|
+
deps.log("Aborted — no server write was attempted.");
|
|
1890
|
+
return 0;
|
|
1891
|
+
}
|
|
1681
1892
|
}
|
|
1682
1893
|
// ---- Consent #3: the EXACT gate change, only when the preview needs it ---
|
|
1683
1894
|
// Never inferred from consent #2: agreeing to install is not agreeing to
|
|
@@ -1709,6 +1920,49 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1709
1920
|
return 1;
|
|
1710
1921
|
}
|
|
1711
1922
|
renderBootstrapOutcome(deps, bootstrapResponse);
|
|
1923
|
+
// ---- BAPI-1391: store the confirmed gate, then offer its qualification --
|
|
1924
|
+
// Stored after the apply so the bootstrap's additive CI write cannot widen
|
|
1925
|
+
// the gate's exact provider checks: the gate PUT rewrites that projection to
|
|
1926
|
+
// exactly the confirmed checks, under the server's revision compare-and-swap.
|
|
1927
|
+
if (storedGate === null) {
|
|
1928
|
+
storedGate = await storeConductorGate(gateIo, access, gatePlan);
|
|
1929
|
+
if (storedGate === null)
|
|
1930
|
+
return 1;
|
|
1931
|
+
}
|
|
1932
|
+
const manualActions = [];
|
|
1933
|
+
if (gatePlan.desired.localCommands.length > 0) {
|
|
1934
|
+
const qualified = storedGate.qualification_state === "qualified";
|
|
1935
|
+
let qualify = options.gate.qualifyGate;
|
|
1936
|
+
if (!qualify && !qualified) {
|
|
1937
|
+
deps.log("");
|
|
1938
|
+
deps.log(" The local gate must pass once under the executor before an unattended run is admitted.");
|
|
1939
|
+
qualify = await confirm(deps, "Qualify gate now? [y/N]: ");
|
|
1940
|
+
}
|
|
1941
|
+
if (qualify) {
|
|
1942
|
+
// Non-fatal by design: a failed qualification warns and the install goes on.
|
|
1943
|
+
await runConductorGateQualification(gateIo, access, deps.gate, {
|
|
1944
|
+
revision: storedGate.revision,
|
|
1945
|
+
baseUrl,
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
else if (!qualified) {
|
|
1949
|
+
manualActions.push(`gate qualification: run ${PRINTED_QUALIFY_GATE_COMMAND} to qualify the local gate; ` +
|
|
1950
|
+
"until then admission refuses it (local_command_unqualified).");
|
|
1951
|
+
}
|
|
1952
|
+
else {
|
|
1953
|
+
deps.log(` local gate already qualified at revision ${storedGate.revision}.`);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
// ---- BAPI-1405: epic-branch CI inspection and the explicit decision ----
|
|
1957
|
+
// Runs after the gate is resolved and stored, inside the gate phase. It only
|
|
1958
|
+
// reports and proposes; it never confirms a gate or authorizes a merge. A
|
|
1959
|
+
// failed read or decision write warns and the install continues.
|
|
1960
|
+
const epicBranchCi = await runEpicBranchCiInspection(gateIo, access, deps.gate, {
|
|
1961
|
+
decisionFlag: options.epicBranchCiDecision ?? null,
|
|
1962
|
+
dryRun: false,
|
|
1963
|
+
});
|
|
1964
|
+
if (epicBranchCi.manualAction !== null)
|
|
1965
|
+
manualActions.push(epicBranchCi.manualAction);
|
|
1712
1966
|
// ---- Phase 3: optional workflow scaffold -------------------------------
|
|
1713
1967
|
deps.log("");
|
|
1714
1968
|
deps.log("Phase 3/8 — claude-review workflow…");
|
|
@@ -1772,7 +2026,6 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1772
2026
|
// Placed after the workflow scaffold and BEFORE setup-epic verification,
|
|
1773
2027
|
// executor provisioning, and the capability report, so a run that stops at a
|
|
1774
2028
|
// later phase has still closed the local gap it consented to close.
|
|
1775
|
-
const manualActions = [];
|
|
1776
2029
|
deps.log("");
|
|
1777
2030
|
deps.log("Phase 4/8 — conductor tool visibility…");
|
|
1778
2031
|
if (local) {
|