@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
|
@@ -92,15 +92,17 @@
|
|
|
92
92
|
* unrelated additive change look like a breaking one here, or worse, hide a real
|
|
93
93
|
* one.
|
|
94
94
|
*/
|
|
95
|
+
import { PRINTED_CHECK_NAME_INSTRUCTION, PRINTED_CONDUCTOR_EXECUTOR_INSTALL_COMMAND, PRINTED_CONDUCTOR_INSTALL_COMMAND, PRINTED_CONNECT_GITHUB_COMMAND, PRINTED_EXECUTOR_ID_INSTRUCTION, PRINTED_QUALIFY_GATE_COMMAND, } from "../printed-remedy-commands.js";
|
|
95
96
|
import { mapConductorDoctorReportToReadinessChecks, } from "./doctor.js";
|
|
96
97
|
import { mapInstallStatusChecksToReadinessChecks, } from "../install-doctor.js";
|
|
97
98
|
import { mapPlanePreflightToReadinessChecks, } from "../plane/preflight.js";
|
|
98
99
|
import { runConductorInstallDoctor, } from "./install-doctor.js";
|
|
99
|
-
import { BRING_UP_SECTION_LABELS, presentRequiredChecks, } from "./readiness-sections.js";
|
|
100
|
+
import { BRING_UP_SECTION_LABELS, describeGateProfile, describeGateProfileWarning, effectiveReviewModeIsRepositoryPolicy, EPIC_BRANCH_CI_SECTION_IDS, epicBranchCiSections, presentRequiredChecks, quotedSubject, } from "./readiness-sections.js";
|
|
100
101
|
import { classifyBringUpFacts } from "./bring-up-facts.js";
|
|
102
|
+
import { CI_WORKFLOW_PATH, REVIEW_WORKFLOW_PATH, ciWorkflowRemedy, repositoryWorkflowRemedy, } from "./repository-workflow-remedy.js";
|
|
101
103
|
import { classifyMergeExecutionEligibility, MERGE_EXECUTION_INELIGIBLE_REMEDIATION, MERGE_EXECUTION_UNVERIFIABLE_REMEDIATION, } from "./merge-execution-eligibility.js";
|
|
102
104
|
import { createReadinessCheck, createReadinessCheckSafely, summarizeReadinessChecks, validateReadinessChecks, } from "../readiness-check.js";
|
|
103
|
-
import { ADMISSION_EVIDENCE_SCHEMA_VERSION, } from "./bridge-api-client.js";
|
|
105
|
+
import { ADMISSION_EVIDENCE_SCHEMA_VERSION, gateProfileWaivesCiWorkflow, } from "./bridge-api-client.js";
|
|
104
106
|
/** This report's own schema version. See the module docstring. */
|
|
105
107
|
export const CONDUCTOR_READINESS_GATE_SCHEMA_VERSION = 1;
|
|
106
108
|
/** Fixed statement carried in every report, so no consumer can mistake it for a gate. */
|
|
@@ -137,7 +139,10 @@ export const SERVER_READINESS_DESCRIPTORS = [
|
|
|
137
139
|
id: "single-transition-authority",
|
|
138
140
|
label: BRING_UP_SECTION_LABELS["single-transition-authority"],
|
|
139
141
|
},
|
|
140
|
-
|
|
142
|
+
// The stable ID stays `reconciler-liveness`; only the label changed (BAPI-1360),
|
|
143
|
+
// because the row reports whether ACTIVE runs are making progress, not whether a
|
|
144
|
+
// reconciler process exists.
|
|
145
|
+
{ id: "reconciler-liveness", label: "Active-run progress" },
|
|
141
146
|
{ id: "executor-readiness", label: "Executor server observation" },
|
|
142
147
|
{ id: "review-workflow", label: "Review workflow protocol" },
|
|
143
148
|
{ id: "conductor-ci-workflow", label: "Conductor CI migration guard" },
|
|
@@ -145,7 +150,7 @@ export const SERVER_READINESS_DESCRIPTORS = [
|
|
|
145
150
|
// third unattended refusal reads. Noun phrases, like every row above: these
|
|
146
151
|
// render as list items under a source heading, not as sentences.
|
|
147
152
|
{ id: "unattended-consent", label: "Repository unattended consent" },
|
|
148
|
-
{ id: "unattended-notify-default", label: "
|
|
153
|
+
{ id: "unattended-notify-default", label: "Escalation channel (webhook or email)" },
|
|
149
154
|
{ id: "repository-workflow-readiness", label: "Repository workflow readiness" },
|
|
150
155
|
];
|
|
151
156
|
/** Fixed, secret-free prose per failure kind. Never a status body or header. */
|
|
@@ -169,23 +174,29 @@ const SERVER_FAILURE_COPY = {
|
|
|
169
174
|
};
|
|
170
175
|
/** Remediations for the individual server facts, as fixed prose. */
|
|
171
176
|
const SERVER_REMEDIATIONS = {
|
|
172
|
-
supervisorSetup:
|
|
173
|
-
supervisorConfig:
|
|
177
|
+
supervisorSetup: `run \`${PRINTED_CONDUCTOR_INSTALL_COMMAND}\` (or \`setup-epic\`) for this repository to write the project-default supervisor posture. ${PRINTED_CHECK_NAME_INSTRUCTION}`,
|
|
178
|
+
supervisorConfig: `run \`${PRINTED_CONDUCTOR_INSTALL_COMMAND}\` for this repository to write the project-default supervisor configuration. ${PRINTED_CHECK_NAME_INSTRUCTION}`,
|
|
174
179
|
mergeApproval: "set the merge-approval requirement explicitly in the supervisor configuration rather than leaving it unset.",
|
|
175
180
|
reviewPolicyMissing: "set the repository review-policy default so the merge path and the code_review gate read the same signal.",
|
|
176
181
|
reviewPolicyDivergent: "align the repository review-policy default with the done gate's review_state condition, or confirm the difference is intended.",
|
|
177
|
-
githubCredentials:
|
|
182
|
+
githubCredentials: `connect GitHub from the Bridge setup UI or run \`${PRINTED_CONNECT_GITHUB_COMMAND}\` so owner, repository id, and installation id all resolve.`,
|
|
178
183
|
actionsProbeFailed: "re-run once credentials resolve; the permission could not be checked, which is not the same as confirmed-missing.",
|
|
179
184
|
actionsMissing: "grant the GitHub App `actions: write`; without it the conductor's workflow rerun lane fails open on 403.",
|
|
180
185
|
reconcilerUnreadable: "retry once the Bridge API is reachable; reconciler liveness is unknown here, not confirmed stale.",
|
|
181
|
-
|
|
186
|
+
// BAPI-1360: the frozen `epic-tick` command is never named. The reconciler runs
|
|
187
|
+
// inside the worker, so the first check is that the worker is up, then the run's
|
|
188
|
+
// own status.
|
|
189
|
+
reconcilerStale: "check the worker first (`server.worker-present`), then inspect the stalled run with `epic-implementer epic-status`.",
|
|
182
190
|
executorUnreadable: "retry once the Bridge API is reachable; no executor readiness claim is ever made from local state.",
|
|
183
|
-
executorNeverSeen:
|
|
191
|
+
executorNeverSeen: `provision and start an executor (\`${PRINTED_CONDUCTOR_EXECUTOR_INSTALL_COMMAND}\` or \`executor install-service\`); readiness comes only from a live server observation. ${PRINTED_CHECK_NAME_INSTRUCTION} ${PRINTED_EXECUTOR_ID_INSTRUCTION}`,
|
|
192
|
+
executorNoneObservedNoRuns: `no action is needed to launch: \`drive-epic\` starts a run-scoped executor. To keep an executor running between runs, install one with \`${PRINTED_CONDUCTOR_EXECUTOR_INSTALL_COMMAND}\` or \`executor install-service\`. ${PRINTED_CHECK_NAME_INSTRUCTION} ${PRINTED_EXECUTOR_ID_INSTRUCTION}`,
|
|
184
193
|
executorStale: "restart the executor service; it was observed once and has gone quiet past the server's staleness threshold.",
|
|
185
194
|
reviewWorkflowUnknown: "re-run; the default branch's review workflow could not be evaluated, which is not the same as it being absent.",
|
|
186
195
|
reviewWorkflowIncomplete: "install the claude-review workflow template on the default branch so it emits both the run id and the reviewed SHA the verdict protocol reads.",
|
|
187
196
|
ciWorkflowUnknown: "re-run; the default branch's conductor CI workflow could not be evaluated, which is not the same as the guard being absent.",
|
|
188
|
-
|
|
197
|
+
// BAPI-1359: the missing-guard remedy is built by `ciWorkflowRemedy` (the shared
|
|
198
|
+
// formatter), because its wording depends on WHY the guard is missing — a
|
|
199
|
+
// confirmed Alembic chain, an older server, or an unreadable answer.
|
|
189
200
|
olderServer: "upgrade the Bridge API deploy so it reports this fact; its state is unknown here, not healthy.",
|
|
190
201
|
// BAPI-1102. Each names the ONE action that clears it, and none of them
|
|
191
202
|
// mentions a webhook URL, a destination, or any configuration value.
|
|
@@ -193,7 +204,10 @@ const SERVER_REMEDIATIONS = {
|
|
|
193
204
|
// admin panel's Setup page. No MCP or CLI surface ever writes it.
|
|
194
205
|
unattendedConsent: "turn on 'Allow unattended Epic Implementer runs' on the repository's Setup page in the Bridge admin panel, or run setup with --attended. If it is already on, contact Bridge support.",
|
|
195
206
|
unattendedNotifyDefault: "supply a --policy-file declaring notify.webhook_url, or add an email address to the account so escalation alerts can be emailed, or run setup with --attended; an unattended run must have a working escalation channel, and notify.local_sink is not one.",
|
|
196
|
-
|
|
207
|
+
// BAPI-1359: the `repository-workflow-readiness` remedy is built by
|
|
208
|
+
// `repositoryWorkflowRemedy` (the shared formatter) from the SAME report, so it
|
|
209
|
+
// names the specific missing item. The generic "run `install-bridge
|
|
210
|
+
// conductor`" text was retired: no install step creates a migration-guard job.
|
|
197
211
|
// BAPI-1135. Both are IMPORTED from the shared classifier rather than written
|
|
198
212
|
// out here, so the remedy command has exactly one spelling across the refusal,
|
|
199
213
|
// the readiness row, and the doctor — see `merge-execution-contract.ts`.
|
|
@@ -331,14 +345,31 @@ export function mapServerReadinessToChecks(readiness, failure = "unreachable") {
|
|
|
331
345
|
label,
|
|
332
346
|
status: outcome.status,
|
|
333
347
|
detail: outcome.detail,
|
|
348
|
+
// A pass carries no remediation, and a `skip` carries one only when its
|
|
349
|
+
// outcome supplied one: the older-server default would misdescribe a fact
|
|
350
|
+
// that was deliberately not evaluated.
|
|
334
351
|
...(outcome.status === "pass"
|
|
335
352
|
? {}
|
|
336
|
-
:
|
|
353
|
+
: outcome.status === "skip"
|
|
354
|
+
? outcome.remediation === undefined
|
|
355
|
+
? {}
|
|
356
|
+
: { remediation: outcome.remediation }
|
|
357
|
+
: { remediation: outcome.remediation ?? SERVER_REMEDIATIONS.olderServer }),
|
|
337
358
|
});
|
|
338
359
|
});
|
|
339
360
|
// BAPI-1260 — appended ONLY when the server reported the block, so every
|
|
340
361
|
// pre-BAPI-1260 report keeps exactly the descriptor set above.
|
|
341
|
-
return [
|
|
362
|
+
return [
|
|
363
|
+
...described,
|
|
364
|
+
...admissionEvidenceChecks(readiness),
|
|
365
|
+
// BAPI-1390 — appended ONLY when the server reported a gate profile.
|
|
366
|
+
...gateProfileChecks(readiness),
|
|
367
|
+
// BAPI-1405 — always three warn-only epic-branch CI rows. An older server
|
|
368
|
+
// that omits the block renders them as "Unreported — upgrade the server",
|
|
369
|
+
// never as "no warning".
|
|
370
|
+
...epicBranchCiChecks(readiness),
|
|
371
|
+
...genericDiagnosticChecks(readiness),
|
|
372
|
+
];
|
|
342
373
|
}
|
|
343
374
|
function genericDiagnosticChecks(readiness) {
|
|
344
375
|
return (readiness.diagnostics ?? []).map((diagnostic, index) => {
|
|
@@ -377,6 +408,190 @@ const ADMISSION_OWNER_REMEDIATION = {
|
|
|
377
408
|
repository_admin: "a repository administrator resolves it (unattended consent is the 'Allow unattended Epic Implementer runs' toggle on the repository's Setup page in the Bridge admin panel); setup never grants it.",
|
|
378
409
|
bridge_operations: "escalate to Bridge operations; this is not something an operator command can change.",
|
|
379
410
|
};
|
|
411
|
+
/** The retired and current Bridge CI gate names, spelled once for the fixed remedies. */
|
|
412
|
+
const RETIRED_GATE_NAME = "conductor-ci / gate";
|
|
413
|
+
const CURRENT_GATE_NAME = "epic-implementer-ci / gate";
|
|
414
|
+
/**
|
|
415
|
+
* Fixed, per-reason remedies (BAPI-1360). Assembled from literals and the two
|
|
416
|
+
* hard-coded workflow paths only: a record's reason picks a template, and nothing
|
|
417
|
+
* from the payload is interpolated. `null` means "no specific remedy — use the
|
|
418
|
+
* owner's generic one".
|
|
419
|
+
*/
|
|
420
|
+
function admissionReasonRemedy(record, readiness) {
|
|
421
|
+
const sup = readiness.supervisor;
|
|
422
|
+
const ciAbsent = sup.required_checks_ci_workflow_absent === true ||
|
|
423
|
+
(readiness.conductor_ci_workflow?.probe_succeeded === true &&
|
|
424
|
+
readiness.conductor_ci_workflow?.workflow_present === false);
|
|
425
|
+
const reviewAbsent = sup.required_checks_review_workflow_absent === true ||
|
|
426
|
+
(readiness.review_workflow?.probe_succeeded === true &&
|
|
427
|
+
readiness.review_workflow?.workflow_present === false);
|
|
428
|
+
switch (record.reason) {
|
|
429
|
+
case "review_workflow_unconfirmed":
|
|
430
|
+
return (`a repository administrator adds ${REVIEW_WORKFLOW_PATH} to the default branch, or repairs it so it ` +
|
|
431
|
+
"emits the run-id: and Reviewed-SHA: markers in its verdict comment; setup never grants it.");
|
|
432
|
+
case "conductor_ci_workflow_unconfirmed":
|
|
433
|
+
return (`a repository administrator adds ${CI_WORKFLOW_PATH} to the default branch, or repairs it (a ` +
|
|
434
|
+
"migration-guard job is required when the repository has alembic.ini); setup never grants it.");
|
|
435
|
+
case "required_checks_lane_workflow_absent": {
|
|
436
|
+
const files = [ciAbsent ? CI_WORKFLOW_PATH : null, reviewAbsent ? REVIEW_WORKFLOW_PATH : null].filter((file) => file !== null);
|
|
437
|
+
const named = files.length > 0 ? files.join(" and ") : `${CI_WORKFLOW_PATH} and ${REVIEW_WORKFLOW_PATH}`;
|
|
438
|
+
return (`a repository administrator adds ${named} to the default branch so the configured required ` +
|
|
439
|
+
`checks (such as ${CURRENT_GATE_NAME}) have a producer; setup never grants it.`);
|
|
440
|
+
}
|
|
441
|
+
case "required_check_retired_gate":
|
|
442
|
+
return (`a repository administrator replaces ${RETIRED_GATE_NAME} with ${CURRENT_GATE_NAME} in the plan's ` +
|
|
443
|
+
"required_checks, in the supervisor done-gate checks, and in branch protection." +
|
|
444
|
+
(ciAbsent ? ` Also add ${CI_WORKFLOW_PATH} to the default branch so ${CURRENT_GATE_NAME} has a producer.` : ""));
|
|
445
|
+
default:
|
|
446
|
+
return gateProfileReasonRemedy(record.reason, record.verdict, record.subject ?? null);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
/** The provider-evidence window the server searches, spelled once for the remedies. */
|
|
450
|
+
const PROVIDER_EVIDENCE_WINDOW = "the last 12 default-branch commits or 8 merged pull-request heads from the last 90 days";
|
|
451
|
+
/**
|
|
452
|
+
* BAPI-1390 — fixed remedies for the four gate-profile reasons. Everything is a
|
|
453
|
+
* literal except the validated `subject`. `null` for any other reason, so an
|
|
454
|
+
* unknown reason falls back to the owner's generic remedy.
|
|
455
|
+
*/
|
|
456
|
+
function gateProfileReasonRemedy(reason, verdict, subject) {
|
|
457
|
+
switch (reason) {
|
|
458
|
+
case "provider_check_unseen":
|
|
459
|
+
return (`a repository administrator confirms that the check ${quotedSubject(subject, "named in the gate profile")} ` +
|
|
460
|
+
`reports on the default branch — it was not seen on ${PROVIDER_EVIDENCE_WINDOW} — or edits the gate ` +
|
|
461
|
+
"profile; setup never grants it.");
|
|
462
|
+
case "provider_evidence_unavailable":
|
|
463
|
+
return (`Bridge could not read the provider's check history for ${quotedSubject(subject, "a gate-profile check")} ` +
|
|
464
|
+
`(${PROVIDER_EVIDENCE_WINDOW}), so it is unknown, not absent. Re-run readiness; if it persists, ` +
|
|
465
|
+
"escalate to Bridge operations.");
|
|
466
|
+
case "local_command_unqualified":
|
|
467
|
+
return verdict === "unable_to_verify"
|
|
468
|
+
? `the qualification of the local gate command ${quotedSubject(subject, "in the gate profile")} could not ` +
|
|
469
|
+
"be read, so it is unknown, not qualified. Re-run readiness; if it persists, escalate to Bridge operations."
|
|
470
|
+
: `the local gate command ${quotedSubject(subject, "in the gate profile")} is not qualified for the current ` +
|
|
471
|
+
"gate-profile revision (unqualified or stale). Qualify it with " +
|
|
472
|
+
`\`${PRINTED_QUALIFY_GATE_COMMAND}\`, or have a repository administrator ` +
|
|
473
|
+
"edit the gate profile.";
|
|
474
|
+
case "migration_guard_unset":
|
|
475
|
+
return verdict === "unable_to_verify"
|
|
476
|
+
? "Bridge could not confirm whether this repository has alembic.ini on its default branch, so it could " +
|
|
477
|
+
"not decide whether the gate profile needs a migration guard. Re-run readiness, or have a repository " +
|
|
478
|
+
"administrator set a migration guard in the gate profile."
|
|
479
|
+
: "this repository has alembic.ini on its default branch, so its gate profile must name a migration " +
|
|
480
|
+
"guard (a provider check or a local command). A repository administrator sets it in the gate " +
|
|
481
|
+
"profile; setup never grants it.";
|
|
482
|
+
default:
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const GATE_PROFILE_WARNING_REMEDIATION = "no action is needed to launch; confirm the check reports once an epic branch runs CI.";
|
|
487
|
+
/**
|
|
488
|
+
* BAPI-1390 — the gate-profile row plus one warn row per warning. Appended
|
|
489
|
+
* ONLY when the server reported the block, so an older server's report is
|
|
490
|
+
* unchanged. The row is descriptive: the server's admission evidence is the
|
|
491
|
+
* authority, and the warning rows never hold a launch.
|
|
492
|
+
*/
|
|
493
|
+
export function gateProfileChecks(readiness) {
|
|
494
|
+
const profile = readiness.gate_profile;
|
|
495
|
+
if (profile === undefined)
|
|
496
|
+
return [];
|
|
497
|
+
if (profile === null) {
|
|
498
|
+
return [
|
|
499
|
+
createReadinessCheckSafely({
|
|
500
|
+
id: "server.gate-profile",
|
|
501
|
+
source: "server",
|
|
502
|
+
label: "Gate profile",
|
|
503
|
+
status: "fail",
|
|
504
|
+
detail: "the server reported no gate profile, so the CI gate is unverified",
|
|
505
|
+
remediation: SERVER_REMEDIATIONS.olderServer,
|
|
506
|
+
}),
|
|
507
|
+
];
|
|
508
|
+
}
|
|
509
|
+
const detail = describeGateProfile(profile);
|
|
510
|
+
let row;
|
|
511
|
+
if (profile.read_failed || profile.stored_malformed) {
|
|
512
|
+
row = createReadinessCheckSafely({
|
|
513
|
+
id: "server.gate-profile",
|
|
514
|
+
source: "server",
|
|
515
|
+
label: "Gate profile",
|
|
516
|
+
status: "fail",
|
|
517
|
+
detail: `${profile.read_failed ? "the stored gate profile could not be read" : "the stored gate profile is malformed"} — ${detail}`,
|
|
518
|
+
remediation: profile.read_failed
|
|
519
|
+
? "re-run readiness; if it persists, escalate to Bridge operations."
|
|
520
|
+
: "a repository administrator re-saves the gate profile; setup never grants it.",
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
else if (profile.verdict === "denied" || profile.verdict === "unable_to_verify") {
|
|
524
|
+
row = createReadinessCheckSafely({
|
|
525
|
+
id: "server.gate-profile",
|
|
526
|
+
source: "server",
|
|
527
|
+
label: "Gate profile",
|
|
528
|
+
status: "fail",
|
|
529
|
+
detail: `${profile.verdict === "denied" ? "DENIED" : "could not be verified"}${profile.reason ? ` (${profile.reason})` : ""} — ${detail}`,
|
|
530
|
+
remediation: gateProfileReasonRemedy(profile.reason, profile.verdict, profile.subject) ??
|
|
531
|
+
(profile.verdict === "denied"
|
|
532
|
+
? ADMISSION_OWNER_REMEDIATION.repository_admin
|
|
533
|
+
: ADMISSION_OWNER_REMEDIATION.bridge_operations),
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
else if (profile.verdict === "confirmed" && profile.kind !== "none") {
|
|
537
|
+
row = createReadinessCheckSafely({
|
|
538
|
+
id: "server.gate-profile",
|
|
539
|
+
source: "server",
|
|
540
|
+
label: "Gate profile",
|
|
541
|
+
status: "pass",
|
|
542
|
+
detail,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
row = createReadinessCheckSafely({
|
|
547
|
+
id: "server.gate-profile",
|
|
548
|
+
source: "server",
|
|
549
|
+
label: "Gate profile",
|
|
550
|
+
status: "warn",
|
|
551
|
+
detail,
|
|
552
|
+
remediation: profile.kind === "none"
|
|
553
|
+
? "no action is needed to launch; to let child merges proceed without approval, a repository administrator adds checks to the gate profile."
|
|
554
|
+
: "a repository administrator confirms the gate profile; the server's launch evidence decides whether a run is held.",
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
const warnings = profile.warnings.map((warning, index) => createReadinessCheckSafely({
|
|
558
|
+
id: `server.gate-profile-warning-${index + 1}`,
|
|
559
|
+
source: "server",
|
|
560
|
+
label: "Gate profile warning",
|
|
561
|
+
status: "warn",
|
|
562
|
+
detail: describeGateProfileWarning(warning.code, warning.name),
|
|
563
|
+
remediation: GATE_PROFILE_WARNING_REMEDIATION,
|
|
564
|
+
}));
|
|
565
|
+
return [row, ...warnings];
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* BAPI-1405 — the three warn-only epic-branch CI rows as `server.*` checks:
|
|
569
|
+
* `server.effective-review-mode`, `server.epic-branch-ci-decision`, and
|
|
570
|
+
* `server.epic-branch-ci-coverage-warning`.
|
|
571
|
+
*
|
|
572
|
+
* Built from the SAME shared sections the install doctor renders, so the two
|
|
573
|
+
* surfaces cannot word one fact apart. Status is only ever `pass` or `warn`:
|
|
574
|
+
* these rows never fail, never hold a launch, and feed no verdict.
|
|
575
|
+
*
|
|
576
|
+
* Unlike `gateProfileChecks`, an absent or `null` block (older server) still
|
|
577
|
+
* yields the three rows, each `warn` with "Unreported — upgrade the server" and
|
|
578
|
+
* the upgrade remediation. Emitting nothing would read as "no warning", which
|
|
579
|
+
* is the wrong answer: the server simply did not say. Omitted and `null` render
|
|
580
|
+
* identically. The install doctor's own copies of these sections are filtered
|
|
581
|
+
* by `INSTALL_SECTION_IDS_OWNED_ELSEWHERE`, so each row still appears once.
|
|
582
|
+
*/
|
|
583
|
+
export function epicBranchCiChecks(readiness) {
|
|
584
|
+
return epicBranchCiSections(readiness.epic_branch_ci).map((section) => createReadinessCheckSafely({
|
|
585
|
+
id: `server.${section.id}`,
|
|
586
|
+
source: "server",
|
|
587
|
+
label: section.label,
|
|
588
|
+
status: section.status === "ok" ? "pass" : "warn",
|
|
589
|
+
detail: section.detail,
|
|
590
|
+
...(section.status === "ok" || section.remediation === undefined
|
|
591
|
+
? {}
|
|
592
|
+
: { remediation: section.remediation }),
|
|
593
|
+
}));
|
|
594
|
+
}
|
|
380
595
|
/**
|
|
381
596
|
* Project the server's typed launch evidence (BAPI-1260) into `server.*`
|
|
382
597
|
* checks, read-only.
|
|
@@ -405,13 +620,30 @@ export function admissionEvidenceChecks(readiness) {
|
|
|
405
620
|
}),
|
|
406
621
|
];
|
|
407
622
|
}
|
|
408
|
-
return evidence.records.map((record) => admissionEvidenceCheck(record));
|
|
623
|
+
return evidence.records.map((record) => admissionEvidenceCheck(record, readiness));
|
|
409
624
|
}
|
|
410
|
-
function admissionEvidenceCheck(record) {
|
|
625
|
+
function admissionEvidenceCheck(record, readiness) {
|
|
411
626
|
const slug = record.predicate.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
412
627
|
const label = ADMISSION_EVIDENCE_LABELS[record.predicate] ?? "Launch evidence";
|
|
413
628
|
const reason = record.reason ? ` (${record.reason})` : "";
|
|
414
|
-
const remediation =
|
|
629
|
+
const remediation = admissionReasonRemedy(record, readiness) ??
|
|
630
|
+
ADMISSION_OWNER_REMEDIATION[record.next_action_owner ?? "operator"] ??
|
|
631
|
+
ADMISSION_OWNER_REMEDIATION.operator;
|
|
632
|
+
// BAPI-1360: the durable-actor handshake being unsupported is a known, current
|
|
633
|
+
// limitation, not a hold — it renders as a warning that says so. Every other
|
|
634
|
+
// predicate, and every other reason for this one, keeps its existing branch.
|
|
635
|
+
if (record.predicate === "durable_actor" && record.reason === "actor_handshake_unsupported") {
|
|
636
|
+
return createReadinessCheckSafely({
|
|
637
|
+
id: `server.admission-${slug}`,
|
|
638
|
+
source: "server",
|
|
639
|
+
label,
|
|
640
|
+
status: "warn",
|
|
641
|
+
detail: `not yet supported by this deploy${reason} — this does not hold a launch`,
|
|
642
|
+
// Fixed, non-escalating: the owner remedy would tell the operator to escalate
|
|
643
|
+
// for a limitation that blocks nothing.
|
|
644
|
+
remediation: "no action is needed to launch; this warning clears once the deploy supports the durable-actor handshake.",
|
|
645
|
+
});
|
|
646
|
+
}
|
|
415
647
|
switch (record.verdict) {
|
|
416
648
|
case "confirmed":
|
|
417
649
|
return createReadinessCheckSafely({
|
|
@@ -503,7 +735,7 @@ function unattendedOutcomes(readiness) {
|
|
|
503
735
|
? {
|
|
504
736
|
id: "unattended-consent",
|
|
505
737
|
status: "pass",
|
|
506
|
-
detail: "this repository has consented to unattended
|
|
738
|
+
detail: "this repository has consented to unattended Epic Implementer runs",
|
|
507
739
|
}
|
|
508
740
|
: {
|
|
509
741
|
id: "unattended-consent",
|
|
@@ -540,13 +772,22 @@ function unattendedOutcomes(readiness) {
|
|
|
540
772
|
? {
|
|
541
773
|
id: "repository-workflow-readiness",
|
|
542
774
|
status: "pass",
|
|
543
|
-
detail: "the review and
|
|
775
|
+
detail: "the review and epic-implementer-ci workflows are both confirmed",
|
|
544
776
|
}
|
|
545
777
|
: {
|
|
546
778
|
id: "repository-workflow-readiness",
|
|
547
779
|
status: "warn",
|
|
548
|
-
|
|
549
|
-
|
|
780
|
+
// BAPI-1360: name a confirmed-missing file separately from a file that
|
|
781
|
+
// could not be read; the remedy already differs by owner.
|
|
782
|
+
detail: readiness.review_workflow?.probe_succeeded === true &&
|
|
783
|
+
readiness.review_workflow?.workflow_present === false
|
|
784
|
+
? `${REVIEW_WORKFLOW_PATH} is missing from the default branch — an unattended auto-merging run will be refused`
|
|
785
|
+
: !gateProfileWaivesCiWorkflow(readiness) &&
|
|
786
|
+
readiness.conductor_ci_workflow?.probe_succeeded === true &&
|
|
787
|
+
readiness.conductor_ci_workflow?.workflow_present === false
|
|
788
|
+
? `${CI_WORKFLOW_PATH} is missing from the default branch — an unattended auto-merging run will be refused`
|
|
789
|
+
: "the review and epic-implementer-ci workflows are not both confirmed — an unattended auto-merging run will be refused",
|
|
790
|
+
remediation: gateProfileAwareRepositoryWorkflowRemedy(readiness),
|
|
550
791
|
},
|
|
551
792
|
];
|
|
552
793
|
}
|
|
@@ -555,6 +796,15 @@ function reviewPolicyOutcome(readiness) {
|
|
|
555
796
|
const s = readiness.supervisor;
|
|
556
797
|
const alignment = s.review_policy_alignment;
|
|
557
798
|
if (alignment !== null) {
|
|
799
|
+
// BAPI-1405 — `repository_policy` requires no review verdict, so a divergent
|
|
800
|
+
// done-gate review signal is not a nag. `invalid` still warns.
|
|
801
|
+
if (alignment.status === "divergent" && effectiveReviewModeIsRepositoryPolicy(readiness)) {
|
|
802
|
+
return {
|
|
803
|
+
id: "review-policy",
|
|
804
|
+
status: "pass",
|
|
805
|
+
detail: "alignment not applicable: the effective review mode is 'repository_policy', which requires no review verdict",
|
|
806
|
+
};
|
|
807
|
+
}
|
|
558
808
|
if (alignment.status === "divergent" || alignment.status === "invalid") {
|
|
559
809
|
return {
|
|
560
810
|
id: "review-policy",
|
|
@@ -677,6 +927,17 @@ function reconcilerOutcome(readiness) {
|
|
|
677
927
|
remediation: SERVER_REMEDIATIONS.reconcilerUnreadable,
|
|
678
928
|
};
|
|
679
929
|
}
|
|
930
|
+
// BAPI-1360 — with no active run there is no progress to judge: the row is an
|
|
931
|
+
// explicit, remedy-free `skip`, not a stale-tick failure about a reconciler that
|
|
932
|
+
// has nothing to reconcile. ONLY a readable response with a strict zero count
|
|
933
|
+
// qualifies; a missing or malformed count keeps the stale/pass reading below.
|
|
934
|
+
if (r.liveness_readable === true && r.active_run_count === 0) {
|
|
935
|
+
return {
|
|
936
|
+
id: "reconciler-liveness",
|
|
937
|
+
status: "skip",
|
|
938
|
+
detail: "no active runs, so there is no run progress to check",
|
|
939
|
+
};
|
|
940
|
+
}
|
|
680
941
|
if (!r.stale) {
|
|
681
942
|
return {
|
|
682
943
|
id: "reconciler-liveness",
|
|
@@ -726,14 +987,41 @@ function executorOutcome(readiness) {
|
|
|
726
987
|
remediation: SERVER_REMEDIATIONS.executorStale,
|
|
727
988
|
};
|
|
728
989
|
}
|
|
990
|
+
// `ready === null`. BAPI-1360: with readable executor AND reconciler liveness and
|
|
991
|
+
// a strict zero active-run count, no executor being present is expected —
|
|
992
|
+
// `drive-epic` starts a run-scoped one — so it is a warning, not a failure.
|
|
993
|
+
if (readiness.reconciler.liveness_readable === true && readiness.reconciler.active_run_count === 0) {
|
|
994
|
+
return {
|
|
995
|
+
id: "executor-readiness",
|
|
996
|
+
status: "warn",
|
|
997
|
+
detail: "no executor was observed; `drive-epic` starts a run-scoped executor at launch, so a durable service is optional",
|
|
998
|
+
remediation: SERVER_REMEDIATIONS.executorNoneObservedNoRuns,
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
729
1001
|
return {
|
|
730
1002
|
id: "executor-readiness",
|
|
731
1003
|
status: "fail",
|
|
732
|
-
detail: "
|
|
1004
|
+
detail: "no executor was observed while runs are active or their count could not be read",
|
|
733
1005
|
remediation: SERVER_REMEDIATIONS.executorNeverSeen,
|
|
734
1006
|
};
|
|
735
1007
|
}
|
|
736
|
-
/**
|
|
1008
|
+
/**
|
|
1009
|
+
* BAPI-1390 — the repository-workflow remedy under the server's gate-profile
|
|
1010
|
+
* rule: with a STORED gate profile the CI lane file is not required, so the
|
|
1011
|
+
* remedy never names it. Without one, this is exactly
|
|
1012
|
+
* {@link repositoryWorkflowRemedy}.
|
|
1013
|
+
*/
|
|
1014
|
+
export function gateProfileAwareRepositoryWorkflowRemedy(readiness) {
|
|
1015
|
+
return repositoryWorkflowRemedy(gateProfileWaivesCiWorkflow(readiness) ? { ...readiness, conductor_ci_workflow: null } : readiness);
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Review-workflow protocol facts. `null` means an older server, not health.
|
|
1019
|
+
*
|
|
1020
|
+
* BAPI-1390: the server now requires the review file only for a run whose review
|
|
1021
|
+
* policy source is `verdict_protocol`. This repository-scoped report carries no
|
|
1022
|
+
* run policy, so the row keeps its existing behavior; the per-run decision is the
|
|
1023
|
+
* server's (`unattended.repository_readiness_confirmed` and admission evidence).
|
|
1024
|
+
*/
|
|
737
1025
|
function reviewWorkflowOutcome(readiness) {
|
|
738
1026
|
const w = readiness.review_workflow;
|
|
739
1027
|
if (w === null) {
|
|
@@ -754,6 +1042,16 @@ function reviewWorkflowOutcome(readiness) {
|
|
|
754
1042
|
remediation: SERVER_REMEDIATIONS.reviewWorkflowUnknown,
|
|
755
1043
|
};
|
|
756
1044
|
}
|
|
1045
|
+
// BAPI-1360 — a SUCCESSFUL probe that found no file is a confirmed absence, kept
|
|
1046
|
+
// apart from an unreadable file (Bridge operations) and from missing markers.
|
|
1047
|
+
if (w.workflow_present === false) {
|
|
1048
|
+
return {
|
|
1049
|
+
id: "review-workflow",
|
|
1050
|
+
status: "fail",
|
|
1051
|
+
detail: `${REVIEW_WORKFLOW_PATH} is missing from the default branch (confirmed)`,
|
|
1052
|
+
remediation: `a repository administrator adds ${REVIEW_WORKFLOW_PATH} to the default branch so it emits both the run id and the reviewed SHA the verdict protocol reads.`,
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
757
1055
|
if (w.emits_run_id && w.emits_reviewed_sha) {
|
|
758
1056
|
return {
|
|
759
1057
|
id: "review-workflow",
|
|
@@ -776,11 +1074,21 @@ function reviewWorkflowOutcome(readiness) {
|
|
|
776
1074
|
/** Conductor-CI migration-guard facts. `null` means an older server. */
|
|
777
1075
|
function ciWorkflowOutcome(readiness) {
|
|
778
1076
|
const c = readiness.conductor_ci_workflow;
|
|
1077
|
+
// BAPI-1390 — a STORED gate profile names its own checks and migration guard,
|
|
1078
|
+
// so the lane file is not required. A healthy file still reports a pass; any
|
|
1079
|
+
// other state is reported as not applicable rather than as a gap.
|
|
1080
|
+
if (gateProfileWaivesCiWorkflow(readiness) && !(c?.probe_succeeded === true && c.migration_guard_present === true)) {
|
|
1081
|
+
return {
|
|
1082
|
+
id: "conductor-ci-workflow",
|
|
1083
|
+
status: "skip",
|
|
1084
|
+
detail: `${CI_WORKFLOW_PATH} is not required: this repository has a stored gate profile that names its own checks`,
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
779
1087
|
if (c === null) {
|
|
780
1088
|
return {
|
|
781
1089
|
id: "conductor-ci-workflow",
|
|
782
1090
|
status: "fail",
|
|
783
|
-
detail: "this Bridge API deploy did not report the
|
|
1091
|
+
detail: "this Bridge API deploy did not report the epic-implementer-ci workflow facts",
|
|
784
1092
|
remediation: SERVER_REMEDIATIONS.olderServer,
|
|
785
1093
|
};
|
|
786
1094
|
}
|
|
@@ -794,18 +1102,51 @@ function ciWorkflowOutcome(readiness) {
|
|
|
794
1102
|
remediation: SERVER_REMEDIATIONS.ciWorkflowUnknown,
|
|
795
1103
|
};
|
|
796
1104
|
}
|
|
797
|
-
|
|
798
|
-
|
|
1105
|
+
// BAPI-1360 — confirmed absence of the file, not a missing migration guard.
|
|
1106
|
+
if (c.workflow_present === false) {
|
|
1107
|
+
return {
|
|
1108
|
+
id: "conductor-ci-workflow",
|
|
1109
|
+
status: "fail",
|
|
1110
|
+
detail: `${CI_WORKFLOW_PATH} is missing from the default branch (confirmed)`,
|
|
1111
|
+
remediation: `a repository administrator adds ${CI_WORKFLOW_PATH} to the default branch. ${ciWorkflowRemedy(c)}`,
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
if (c.migration_guard_present) {
|
|
1115
|
+
return {
|
|
799
1116
|
id: "conductor-ci-workflow",
|
|
800
1117
|
status: "pass",
|
|
801
1118
|
detail: "the default branch's conductor CI workflow runs the migration guard",
|
|
802
|
-
}
|
|
803
|
-
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
// BAPI-1359 — the guard is conditional on an Alembic chain. Three explicit
|
|
1122
|
+
// server answers, plus the older-server case where neither field is reported.
|
|
1123
|
+
if (c.migration_chain_probe_succeeded === true && c.migration_guard_required === false) {
|
|
1124
|
+
return {
|
|
804
1125
|
id: "conductor-ci-workflow",
|
|
805
|
-
status: "
|
|
806
|
-
|
|
807
|
-
|
|
1126
|
+
status: "pass",
|
|
1127
|
+
// States only what was observed. It does not say migrations are safe, or
|
|
1128
|
+
// that the repository has none.
|
|
1129
|
+
detail: "migration guard not required (no Alembic chain: no alembic.ini on the default branch)",
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
if (c.migration_chain_probe_succeeded === false) {
|
|
1133
|
+
return {
|
|
1134
|
+
id: "conductor-ci-workflow",
|
|
1135
|
+
status: "fail",
|
|
1136
|
+
detail: "the Alembic check could not be read, so it is not known whether the conductor CI workflow needs the migration guard",
|
|
1137
|
+
remediation: ciWorkflowRemedy(c),
|
|
808
1138
|
};
|
|
1139
|
+
}
|
|
1140
|
+
return {
|
|
1141
|
+
id: "conductor-ci-workflow",
|
|
1142
|
+
status: "warn",
|
|
1143
|
+
detail: c.migration_guard_required === true && c.migration_chain_probe_succeeded === true
|
|
1144
|
+
? "the conductor CI workflow does not run the migration guard, which this repository requires because it has alembic.ini on its default branch"
|
|
1145
|
+
: // An older server (no new fields) or a partial report: keep today's
|
|
1146
|
+
// wording and today's behavior, and claim nothing about alembic.ini.
|
|
1147
|
+
"the conductor CI workflow does not run the migration guard",
|
|
1148
|
+
remediation: ciWorkflowRemedy(c),
|
|
1149
|
+
};
|
|
809
1150
|
}
|
|
810
1151
|
// ---------------------------------------------------------------------------
|
|
811
1152
|
// Install-time section adapter
|
|
@@ -825,6 +1166,15 @@ const INSTALL_SECTION_IDS_OWNED_ELSEWHERE = new Set([
|
|
|
825
1166
|
"supervisor-configuration",
|
|
826
1167
|
"supervisor-required-checks",
|
|
827
1168
|
"supervisor-review-alignment",
|
|
1169
|
+
// BAPI-1390 — owned by `gateProfileChecks`.
|
|
1170
|
+
"supervisor-gate-profile",
|
|
1171
|
+
"supervisor-gate-profile-epic-branch-unseen",
|
|
1172
|
+
"supervisor-gate-profile-epic-branch-search",
|
|
1173
|
+
"supervisor-gate-profile-warning",
|
|
1174
|
+
// BAPI-1405 — owned by `epicBranchCiChecks`.
|
|
1175
|
+
EPIC_BRANCH_CI_SECTION_IDS.reviewMode,
|
|
1176
|
+
EPIC_BRANCH_CI_SECTION_IDS.decision,
|
|
1177
|
+
EPIC_BRANCH_CI_SECTION_IDS.coverage,
|
|
828
1178
|
"github-credentials",
|
|
829
1179
|
"github-actions-permission",
|
|
830
1180
|
"reconciler-liveness",
|
|
@@ -873,7 +1223,7 @@ export function mapInstallSectionsToReadinessChecks(sections) {
|
|
|
873
1223
|
? {}
|
|
874
1224
|
: {
|
|
875
1225
|
remediation: section.remediation ??
|
|
876
|
-
|
|
1226
|
+
`run \`${PRINTED_CONDUCTOR_INSTALL_COMMAND}\` for this repository to re-provision this capability. ${PRINTED_CHECK_NAME_INSTRUCTION}`,
|
|
877
1227
|
}),
|
|
878
1228
|
}));
|
|
879
1229
|
}
|
|
@@ -903,13 +1253,16 @@ export async function collectConductorReadinessGate(deps) {
|
|
|
903
1253
|
}
|
|
904
1254
|
// A plane preflight was requested and could not be run. Not requesting one is
|
|
905
1255
|
// not an unavailability — its prerequisites are still reported as unknowns.
|
|
906
|
-
if (deps.runPlanePreflight && planeResult === null)
|
|
1256
|
+
if (deps.runPlanePreflight && deps.planeNotApplicable === undefined && planeResult === null) {
|
|
907
1257
|
unavailableSources.push("plane");
|
|
1258
|
+
}
|
|
908
1259
|
const checks = [
|
|
909
1260
|
...mapInstallStatusChecksToReadinessChecks((installReport?.legacyInstallChecks ?? null), installReport === null),
|
|
910
1261
|
...mapConductorDoctorReportToReadinessChecks(installReport?.legacyConductor ?? null),
|
|
911
1262
|
...mapInstallSectionsToReadinessChecks(installReport?.sections ?? null),
|
|
912
|
-
...mapPlanePreflightToReadinessChecks(planeResult
|
|
1263
|
+
...mapPlanePreflightToReadinessChecks(planeResult, deps.planeNotApplicable, {
|
|
1264
|
+
...(deps.epicRunId !== undefined ? { epicRunId: deps.epicRunId } : {}),
|
|
1265
|
+
}),
|
|
913
1266
|
...mapServerReadinessToChecks(installReport?.readiness ?? null, classifyServerFailure(installReport)),
|
|
914
1267
|
];
|
|
915
1268
|
// Validate the ASSEMBLED report, not just the pieces: an injected collector or
|
|
@@ -946,6 +1299,8 @@ async function runInstallDoctorSafely(deps) {
|
|
|
946
1299
|
}
|
|
947
1300
|
/** Run the plane preflight, containing any throw. */
|
|
948
1301
|
async function runPlaneSafely(deps) {
|
|
1302
|
+
if (deps.planeNotApplicable !== undefined)
|
|
1303
|
+
return null;
|
|
949
1304
|
if (!deps.runPlanePreflight)
|
|
950
1305
|
return null;
|
|
951
1306
|
try {
|