@bridge_gpt/mcp-server 0.2.66 → 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 +421 -83
- package/build/agent-capabilities/probes.js +15 -5
- package/build/agent-launchers/claude-executor-adapter.js +70 -16
- package/build/agent-launchers/executor-adapter.js +7 -4
- 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/bounded-wait.js +67 -10
- 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 +12 -12
- package/build/conductor/bridge-api-client.js +857 -1
- package/build/conductor/bring-up-facts.js +1 -1
- package/build/conductor/command-drift-production.js +92 -0
- package/build/conductor/command-drift.js +89 -0
- package/build/conductor/doctor.js +72 -29
- package/build/conductor/epic-branch-ci-inspection.js +1375 -0
- package/build/conductor/epic-implementer-cli.js +98 -28
- package/build/conductor/errors.js +4 -3
- package/build/conductor/gate-discovery.js +890 -0
- package/build/conductor/guided-repair.js +6 -2
- package/build/conductor/hook-runtime-qualification.js +1 -1
- package/build/conductor/install-doctor.js +15 -3
- package/build/conductor/json-output.js +63 -0
- package/build/conductor/merge-execution-contract.js +27 -8
- package/build/conductor/merge-execution-eligibility.js +3 -2
- package/build/conductor/pr-ci-producer.js +1 -1
- package/build/conductor/readiness-cli.js +113 -12
- package/build/conductor/readiness-sections.js +410 -9
- package/build/conductor/readiness.js +388 -33
- package/build/conductor/recovery-cli.js +310 -12
- package/build/conductor/recovery-operations.js +167 -3
- package/build/conductor/repair-contract.js +54 -3
- package/build/conductor/repository-workflow-remedy.js +125 -0
- package/build/conductor/tools.js +2 -2
- package/build/conductor-bundle-artifacts.js +8 -0
- 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 +62 -3
- package/build/doctor.js +92 -1
- package/build/drive-epic-key.js +6 -5
- package/build/drive-epic.js +129 -61
- package/build/epic-coherence-input.js +2 -1
- package/build/epic-coherence-input.test-fixtures.js +4 -3
- package/build/epic-implementer-bin.js +1379 -28
- package/build/epic-implementer-bundle-cli.js +2 -2
- package/build/epic-workarounds-path.js +39 -0
- package/build/executor/cli.js +33 -2
- package/build/executor/completion-contract.js +66 -10
- package/build/executor/completion-supervision.js +4 -0
- package/build/executor/deps.js +4 -1
- package/build/executor/env.js +19 -0
- package/build/executor/gate-qualification-job.js +499 -0
- package/build/executor/guarded-fast-forward-push.js +129 -0
- package/build/executor/heartbeat.js +1 -1
- package/build/executor/http-client.js +20 -0
- package/build/executor/integration-job.js +122 -8
- package/build/executor/job-errors.js +33 -1
- package/build/executor/job-runner.js +813 -87
- package/build/executor/likely-tests.js +290 -0
- package/build/executor/live-worker-registry.js +1 -1
- package/build/executor/observation.js +37 -3
- 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 +112 -26
- 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 +111 -18
- package/build/executor/resume-pre-spawn.js +2 -0
- package/build/executor/retirement.js +69 -0
- package/build/executor/runner.js +122 -13
- package/build/executor/service-unit.js +7 -1
- 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 +113 -34
- package/build/executor/worker-guard-hook.js +316 -58
- package/build/executor/worker-log.js +4 -3
- package/build/executor/worker-verification-guidance.js +37 -0
- package/build/executor/worktree.js +166 -8
- 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 +1224 -1054
- package/build/init.js +54 -177
- package/build/install-bridge-conductor-gate.js +1372 -0
- package/build/install-bridge-conductor.js +296 -41
- package/build/install-bridge.js +547 -40
- package/build/mcp-install-state.js +50 -4
- package/build/merge-workflow-install.js +3 -4
- package/build/phase-context-git-probe.js +68 -0
- package/build/phase-result-artifacts.js +173 -5
- package/build/pipeline-utils.js +6 -2
- package/build/pipelines.generated.js +34 -160
- package/build/plan-execution-ledger.js +2 -32
- package/build/plane/alembic-head.js +12 -3
- package/build/plane/base-worktree-guard.js +147 -0
- package/build/plane/cli.js +217 -17
- package/build/plane/code-snapshot.js +468 -0
- package/build/plane/defaults.js +48 -3
- package/build/plane/endpoint.js +41 -0
- package/build/plane/manifest.js +136 -2
- package/build/plane/member-roster.js +94 -19
- package/build/plane/preflight.js +83 -2
- package/build/plane/process-cwd.js +67 -0
- package/build/plane/restart.js +542 -0
- package/build/plane/shutdown.js +31 -0
- package/build/plane/status.js +143 -8
- package/build/plane/supervisor.js +586 -179
- package/build/plane/types.js +54 -2
- package/build/printed-remedy-commands.js +52 -0
- package/build/readiness-check.js +1 -1
- package/build/readme.generated.js +1 -1
- package/build/repair-assets.js +93 -0
- package/build/review-generation.js +22 -0
- package/build/run-unit-tests-launcher.js +130 -49
- package/build/secret-redaction.js +33 -0
- package/build/setup-epic.js +152 -22
- package/build/sfcc/config.js +9 -0
- package/build/sfcc/credentials.js +100 -38
- package/build/sfcc/hostname.js +67 -0
- package/build/sfcc/permissions.js +17 -13
- package/build/sfcc/register.js +1 -1
- package/build/sfcc/tool-wrapper.js +39 -43
- package/build/sfcc/write-grants.js +17 -11
- package/build/sfcc/write-result.js +4 -3
- package/build/sfcc/writes-site-preference.js +20 -8
- package/build/start-tickets-prereqs.js +70 -16
- package/build/start-tickets.js +208 -36
- package/build/tool-error-envelope.js +61 -0
- package/build/tool-surface-gating.js +36 -0
- package/build/upgrade-cli.js +55 -1
- package/build/version.generated.js +3 -3
- package/build/visual-diff.js +22 -1
- package/build/worker-guard-hook-bin.js +4 -6
- package/build/worker-guard-hook-main.js +47 -0
- package/build/worktree-core.js +145 -6
- package/docs/EPIC-IMPLEMENTER-INSTALL.md +867 -0
- package/docs/EPIC-IMPLEMENTER.md +728 -0
- package/docs/install/mcp-tool-integrations.md +24 -15
- package/docs/install/sfcc-integration.md +12 -4
- package/package.json +3 -3
- package/pipelines/greenfield-setup.json +4 -128
- package/pipelines/implement-ticket.json +3 -2
- package/pipelines/plan-epic.json +0 -5
- package/pipelines/review-ticket.json +1 -1
- package/docs/CONDUCTOR.md +0 -605
- package/pipelines/idea-to-pr.json +0 -49
|
@@ -106,6 +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, 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";
|
|
109
111
|
import { bootstrapConductorSupervisorDefaults, classifySupervisorBootstrapFailure, fetchConductorReadiness, previewConductorSupervisorBootstrap, } from "./conductor/bridge-api-client.js";
|
|
110
112
|
import { CONDUCTOR_OPERATOR_RUNBOOK_POINTER, EXECUTOR_PROVISIONING_GUIDANCE, conductorInstallDoctorExitCode, conductorInstallDoctorHasFatal, formatConductorInstallDoctorReport, } from "./conductor/install-doctor.js";
|
|
111
113
|
import { describeNativeLedgerAvailability, } from "./conductor/doctor.js";
|
|
@@ -130,28 +132,76 @@ export const CONDUCTOR_REVIEW_POLICY_SOURCES = [
|
|
|
130
132
|
"verdict_protocol",
|
|
131
133
|
"native_review_decision",
|
|
132
134
|
"none",
|
|
135
|
+
"repository_policy",
|
|
133
136
|
];
|
|
134
|
-
/**
|
|
135
|
-
|
|
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";
|
|
136
147
|
/** Usage text for `install-bridge conductor`. */
|
|
137
148
|
export function getInstallBridgeConductorUsage() {
|
|
138
149
|
return [
|
|
139
150
|
"Usage: install conductor [options]",
|
|
140
151
|
"",
|
|
141
|
-
"Bootstrap Epic
|
|
152
|
+
"Bootstrap the Epic Implementer for this repository: run the unified read-only",
|
|
142
153
|
"doctor, write the safe project-default supervisor posture, optionally scaffold",
|
|
143
154
|
"the claude-review workflow, report the capability matrix, and re-run the doctor.",
|
|
144
155
|
"",
|
|
145
156
|
"Options:",
|
|
146
157
|
" --repo <name> Repository name (default: BAPI_REPO_NAME / .bridge/config)",
|
|
147
|
-
" --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.",
|
|
148
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
|
+
"",
|
|
149
193
|
` --review-policy <source> One of: ${CONDUCTOR_REVIEW_POLICY_SOURCES.join(", ")}`,
|
|
150
|
-
|
|
151
|
-
"
|
|
152
|
-
"
|
|
153
|
-
"
|
|
154
|
-
" 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.",
|
|
155
205
|
" --epic-key <KEY> Epic key for the setup-epic verification phase",
|
|
156
206
|
" --plan-file <path> Plan sidecar for the setup-epic verification phase",
|
|
157
207
|
" --executor-id <id> Stable executor id. Supplying it OPTS IN to the",
|
|
@@ -183,9 +233,10 @@ export function getInstallBridgeConductorUsage() {
|
|
|
183
233
|
" approves one named server and has no default.",
|
|
184
234
|
" --execution-mode <mode> local | durable (default: durable with",
|
|
185
235
|
" --executor-id, otherwise local).",
|
|
186
|
-
" --policy-file <path> Run policy the profile references
|
|
187
|
-
" run
|
|
188
|
-
" API server
|
|
236
|
+
" --policy-file <path> Run policy the profile references. An unattended",
|
|
237
|
+
" run escalates to a notify.webhook_url reachable",
|
|
238
|
+
" from the API server or, with none declared, to the",
|
|
239
|
+
" account's email (notify.local_sink is not enough).",
|
|
189
240
|
" --dry-run Diagnose and preview only. Performs NO writes:",
|
|
190
241
|
" no POST, no file write, no host MCP config write,",
|
|
191
242
|
" no Git hook write, no install-state or capability",
|
|
@@ -194,6 +245,11 @@ export function getInstallBridgeConductorUsage() {
|
|
|
194
245
|
" start-consent prompt.",
|
|
195
246
|
" Still asks the base-URL confirmation below first —",
|
|
196
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.",
|
|
197
253
|
" -h, --help Show this help",
|
|
198
254
|
"",
|
|
199
255
|
"The resolved BAPI_BASE_URL is displayed and confirmed BEFORE any network call",
|
|
@@ -208,7 +264,7 @@ export function getInstallBridgeConductorUsage() {
|
|
|
208
264
|
"nor changed. No file is ever created: when no supported config carries a",
|
|
209
265
|
"a Bridge entry, the run says so and points at the base `install` command.",
|
|
210
266
|
"The MCP server captures its active groups at startup, so a merge activates on",
|
|
211
|
-
"the next launch — restart your MCP client to see the
|
|
267
|
+
"the next launch — restart your MCP client to see the Epic Implementer tools.",
|
|
212
268
|
"",
|
|
213
269
|
"Local observability (a SEPARATE consent): installs or updates this",
|
|
214
270
|
"repository's managed conductor Git hooks (post-commit,",
|
|
@@ -281,10 +337,12 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
281
337
|
let secretName;
|
|
282
338
|
let branches;
|
|
283
339
|
const requiredChecks = [];
|
|
340
|
+
const gate = emptyConductorGateFlags();
|
|
284
341
|
let launchProfile;
|
|
285
342
|
let baseUrl;
|
|
286
343
|
let executionMode;
|
|
287
344
|
let policyFile;
|
|
345
|
+
let epicBranchCiDecision;
|
|
288
346
|
const takeValue = (arg, inline, next) => {
|
|
289
347
|
if (inline !== undefined) {
|
|
290
348
|
if (inline.trim().length === 0)
|
|
@@ -322,6 +380,21 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
322
380
|
return { status: "error", message: "--repo-wide takes no value" };
|
|
323
381
|
repoWide = true;
|
|
324
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;
|
|
325
398
|
case "--repo":
|
|
326
399
|
case "--required-check":
|
|
327
400
|
case "--required-checks":
|
|
@@ -336,7 +409,12 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
336
409
|
case "--launch-profile":
|
|
337
410
|
case "--base-url":
|
|
338
411
|
case "--execution-mode":
|
|
339
|
-
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": {
|
|
340
418
|
const taken = takeValue(flag, inline, argv[i + 1]);
|
|
341
419
|
if (!taken.ok)
|
|
342
420
|
return { status: "error", message: taken.error };
|
|
@@ -345,8 +423,35 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
345
423
|
const value = taken.value;
|
|
346
424
|
if (flag === "--repo")
|
|
347
425
|
repo = value;
|
|
348
|
-
else if (flag === "--required-check")
|
|
426
|
+
else if (flag === "--required-check" || flag === "--provider-check")
|
|
349
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
|
+
}
|
|
350
455
|
else if (flag === "--required-checks")
|
|
351
456
|
requiredChecks.push(...splitList(value));
|
|
352
457
|
else if (flag === "--epic-key")
|
|
@@ -405,9 +510,12 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
405
510
|
return { status: "error", message: `Unknown option: ${flag}` };
|
|
406
511
|
}
|
|
407
512
|
}
|
|
408
|
-
// Normalize + validate the
|
|
409
|
-
// never reach
|
|
410
|
-
//
|
|
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.
|
|
411
519
|
const normalizedChecks = [];
|
|
412
520
|
const seen = new Set();
|
|
413
521
|
for (const check of requiredChecks) {
|
|
@@ -417,13 +525,10 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
417
525
|
seen.add(check);
|
|
418
526
|
normalizedChecks.push(check);
|
|
419
527
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
"so the bootstrap refuses to write one.",
|
|
425
|
-
};
|
|
426
|
-
}
|
|
528
|
+
gate.providerChecks = normalizedChecks;
|
|
529
|
+
const gateError = validateConductorGateFlags(gate, { replaceDoneGate });
|
|
530
|
+
if (gateError !== null)
|
|
531
|
+
return { status: "error", message: gateError };
|
|
427
532
|
// BAPI-1137 — the claim scope is validated HERE, at parse time, for the same
|
|
428
533
|
// reason the required-check list is: a unit that cannot start is worse than a
|
|
429
534
|
// refused invocation, and discovering it costs a reboot to notice.
|
|
@@ -485,6 +590,7 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
485
590
|
repo,
|
|
486
591
|
dryRun,
|
|
487
592
|
requiredChecks: normalizedChecks,
|
|
593
|
+
gate,
|
|
488
594
|
reviewPolicy,
|
|
489
595
|
reviewPolicyExplicit,
|
|
490
596
|
epicKey,
|
|
@@ -503,6 +609,7 @@ export function parseInstallBridgeConductorArgs(argv) {
|
|
|
503
609
|
? { executionMode: executionMode ?? (executorId !== undefined ? "durable" : "local") }
|
|
504
610
|
: {}),
|
|
505
611
|
...(policyFile ? { policyFile } : {}),
|
|
612
|
+
...(epicBranchCiDecision ? { epicBranchCiDecision } : {}),
|
|
506
613
|
},
|
|
507
614
|
};
|
|
508
615
|
}
|
|
@@ -688,12 +795,21 @@ async function confirm(deps, promptText) {
|
|
|
688
795
|
}
|
|
689
796
|
function renderCapabilityMatrix(deps, readiness) {
|
|
690
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");
|
|
691
807
|
// Derived from READINESS, never from the bootstrap's HTTP status: a 200 proves
|
|
692
808
|
// the write was accepted, not that the resulting posture is safe.
|
|
693
809
|
const configurationReady = readiness !== null &&
|
|
694
810
|
readiness.supervisor.setup_present &&
|
|
695
811
|
readiness.supervisor.config_present &&
|
|
696
|
-
!readiness.supervisor.required_checks_empty;
|
|
812
|
+
(!readiness.supervisor.required_checks_empty || confirmedCiFreeGate);
|
|
697
813
|
// `ready === null` (never provisioned) and `false` (stale) are both "not ready"
|
|
698
814
|
// here, but only a recent server-observed claim/heartbeat counts as ready — a
|
|
699
815
|
// configured command or a local process does not.
|
|
@@ -704,6 +820,14 @@ function renderCapabilityMatrix(deps, readiness) {
|
|
|
704
820
|
deps.log(` api_ready: ${apiReady}`);
|
|
705
821
|
deps.log(` configuration_ready: ${configurationReady}`);
|
|
706
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
|
+
}
|
|
707
831
|
deps.log(` executor provisioning: ${EXECUTOR_PROVISIONING_GUIDANCE}`);
|
|
708
832
|
deps.log(` runbook: ${CONDUCTOR_OPERATOR_RUNBOOK_POINTER}`);
|
|
709
833
|
return { apiReady, configurationReady, executorReady };
|
|
@@ -939,7 +1063,7 @@ export function formatConductorRestartAdvisory(mergedProfile) {
|
|
|
939
1063
|
"client to gain access to the Epic Implementer tools.");
|
|
940
1064
|
}
|
|
941
1065
|
/** Remediation printed when no supported host config carries a Bridge entry. */
|
|
942
|
-
export const CONDUCTOR_PROFILE_EMPTY_MERGE_REMEDIATION = `run
|
|
1066
|
+
export const CONDUCTOR_PROFILE_EMPTY_MERGE_REMEDIATION = `run \`${PRINTED_INSTALL_COMMAND}\` to create and register the host MCP entry, ` +
|
|
943
1067
|
"then re-run `install conductor`.";
|
|
944
1068
|
/** Remediation printed whenever the managed conductor hooks were not installed. */
|
|
945
1069
|
export const CONDUCTOR_HOOKS_MANUAL_REMEDIATION = "re-run `install conductor` and accept the local-observability consent, or run " +
|
|
@@ -1503,6 +1627,27 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1503
1627
|
const accessResult = await deps.resolveAccess(options.repo, baseUrl);
|
|
1504
1628
|
const access = accessResult.ok ? accessResult.access : null;
|
|
1505
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
|
+
}
|
|
1506
1651
|
// ---- Phase 1: unified doctor, BEFORE every write -----------------------
|
|
1507
1652
|
deps.log("Phase 1/8 — running the unified epic-implementer doctor (read-only)…");
|
|
1508
1653
|
const firstReport = await deps.runDoctor({
|
|
@@ -1525,8 +1670,10 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1525
1670
|
"default and stamped into a new run that sets no review policy of its own. " +
|
|
1526
1671
|
"An explicit per-run policy always wins, and changing this later never " +
|
|
1527
1672
|
"alters an already-approved run."
|
|
1528
|
-
: "
|
|
1529
|
-
"
|
|
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.");
|
|
1530
1677
|
// ---- Executor preflight: AFTER the URL consent, BEFORE any mutation ----
|
|
1531
1678
|
// Placed here so an unsupported host or a missing `wt`/`git`/`claude` refuses
|
|
1532
1679
|
// before the bootstrap POST, the workflow write, or a single filesystem
|
|
@@ -1575,15 +1722,52 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1575
1722
|
deps.log(" executor provisioning skipped — pass --executor-id <id> to write and start a " +
|
|
1576
1723
|
"service unit for this host.");
|
|
1577
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;
|
|
1578
1743
|
// ---- Dry run: preview and RETURN before every write/prompt -------------
|
|
1579
1744
|
if (options.dryRun) {
|
|
1580
1745
|
deps.log("");
|
|
1581
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.");
|
|
1582
1766
|
deps.log(" Would PREVIEW, then POST /jira/epic-runs/supervisor-bootstrap with field NAMES:");
|
|
1583
1767
|
// Field NAMES and a COUNT only — never the check values, which are
|
|
1584
1768
|
// configuration content, and never the API key.
|
|
1585
1769
|
deps.log(" - done_gate_config.required_checks " +
|
|
1586
|
-
`(${
|
|
1770
|
+
`(${bootstrapChecks.length} check(s))`);
|
|
1587
1771
|
if (options.reviewPolicyExplicit)
|
|
1588
1772
|
deps.log(" - review_policy");
|
|
1589
1773
|
if (options.replaceDoneGate)
|
|
@@ -1634,10 +1818,10 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1634
1818
|
deps.log("complete-with-manual-steps (dry run)");
|
|
1635
1819
|
return 0;
|
|
1636
1820
|
}
|
|
1637
|
-
if (
|
|
1638
|
-
// Unreachable
|
|
1639
|
-
//
|
|
1640
|
-
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.");
|
|
1641
1825
|
return 1;
|
|
1642
1826
|
}
|
|
1643
1827
|
// ---- Phase 2: bootstrap — preview, consent, exact approval, apply -------
|
|
@@ -1648,7 +1832,7 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1648
1832
|
deps.log("");
|
|
1649
1833
|
deps.log("Phase 2/8 — bootstrapping supervisor project defaults…");
|
|
1650
1834
|
const bootstrapRequest = {
|
|
1651
|
-
required_checks:
|
|
1835
|
+
required_checks: bootstrapChecks,
|
|
1652
1836
|
// BAPI-694 — the narrow ReviewPolicy object, sent ONLY on an explicit
|
|
1653
1837
|
// selection. `review_source` is deliberately NOT sent: on an existing
|
|
1654
1838
|
// repository an omitted field keeps its stored value, and a new repository
|
|
@@ -1659,6 +1843,32 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1659
1843
|
merge_approval_required: "true",
|
|
1660
1844
|
...(options.replaceDoneGate ? { replace_done_gate: true } : {}),
|
|
1661
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
|
+
}
|
|
1662
1872
|
let preview;
|
|
1663
1873
|
try {
|
|
1664
1874
|
preview = await previewConductorSupervisorBootstrap(access, bootstrapRequest, deps.postJson);
|
|
@@ -1671,11 +1881,14 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1671
1881
|
// ---- Consent #2: the bootstrap write itself ----------------------------
|
|
1672
1882
|
// Deliberately separate from the base-URL consent above (which was given
|
|
1673
1883
|
// before any network call). Confirming WHERE to write is not consent TO write.
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
deps
|
|
1678
|
-
|
|
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
|
+
}
|
|
1679
1892
|
}
|
|
1680
1893
|
// ---- Consent #3: the EXACT gate change, only when the preview needs it ---
|
|
1681
1894
|
// Never inferred from consent #2: agreeing to install is not agreeing to
|
|
@@ -1707,6 +1920,49 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1707
1920
|
return 1;
|
|
1708
1921
|
}
|
|
1709
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);
|
|
1710
1966
|
// ---- Phase 3: optional workflow scaffold -------------------------------
|
|
1711
1967
|
deps.log("");
|
|
1712
1968
|
deps.log("Phase 3/8 — claude-review workflow…");
|
|
@@ -1770,7 +2026,6 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
1770
2026
|
// Placed after the workflow scaffold and BEFORE setup-epic verification,
|
|
1771
2027
|
// executor provisioning, and the capability report, so a run that stops at a
|
|
1772
2028
|
// later phase has still closed the local gap it consented to close.
|
|
1773
|
-
const manualActions = [];
|
|
1774
2029
|
deps.log("");
|
|
1775
2030
|
deps.log("Phase 4/8 — conductor tool visibility…");
|
|
1776
2031
|
if (local) {
|