@bridge_gpt/mcp-server 0.2.69 → 0.2.70
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 +140 -292
- package/build/agent-launchers/claude-executor-adapter.js +17 -4
- package/build/agents.generated.js +4 -4
- package/build/artifact-diff.js +110 -0
- package/build/bin-entrypoint.js +36 -0
- package/build/bridge-permissions.js +23 -4
- package/build/claude-review-workflow-drift.js +3 -3
- package/build/codex-bridge-profile.js +1 -1
- package/build/commands.generated.js +6 -6
- package/build/conductor/bridge-api-client.js +598 -19
- package/build/conductor/bring-up-facts.js +9 -9
- package/build/conductor/doctor.js +466 -18
- package/build/conductor/epic-child-continuation.js +54 -3
- package/build/conductor/epic-implementer-cli.js +132 -34
- package/build/conductor/epic-runtime.js +2 -2
- package/build/conductor/epic-state.js +1 -1
- package/build/conductor/hook-runtime-qualification.js +1 -1
- package/build/conductor/install-doctor.js +4 -2
- package/build/conductor/launch-need-probes.js +337 -0
- package/build/conductor/local-merge.js +241 -56
- package/build/conductor/merge-credential.js +172 -0
- package/build/conductor/merge-execution-eligibility.js +1 -1
- package/build/conductor/merged-child-check.js +95 -0
- package/build/conductor/plan-trim.js +30 -0
- package/build/conductor/pr-ci-producer.js +1 -1
- package/build/conductor/readiness-cli.js +11 -6
- package/build/conductor/readiness-prerequisites.js +41 -0
- package/build/conductor/readiness-sections.js +345 -55
- package/build/conductor/readiness.js +99 -87
- package/build/conductor/recovery-cli.js +606 -21
- package/build/conductor/recovery-operations.js +306 -7
- package/build/conductor/repair-contract.js +47 -2
- package/build/conductor/repository-workflow-remedy.js +8 -6
- package/build/conductor/stored-merge-credential.js +76 -0
- package/build/conductor/stored-merge-token-probe.js +28 -0
- package/build/conductor/temporary-plan.js +50 -0
- package/build/conductor/wait-run-cli.js +759 -0
- package/build/conductor/worker-database-doctor-inspector.js +49 -0
- package/build/conductor-bin.js +2 -2
- package/build/conductor-claude-hook-bin.js +2 -2
- package/build/conductor-claude-hook-removed-stub-bin.js +2 -5
- package/build/conductor-removed-stub-bin.js +2 -5
- package/build/credential-store.js +356 -13
- package/build/docs.generated.js +26 -13
- package/build/doctor.js +94 -32
- package/build/drive-epic-help.js +212 -0
- package/build/drive-epic.js +1840 -373
- package/build/epic-ci-workflow-drift.js +77 -0
- package/build/epic-ci-workflow-install.js +183 -0
- package/build/epic-ci-workflow.js +157 -0
- package/build/epic-contract-context.js +326 -0
- package/build/epic-implementer-bin.js +57 -41
- package/build/epic-implementer-claude-hook-bin.js +2 -2
- package/build/escalation-reachability.js +134 -0
- package/build/executor/cleanup-report.js +99 -0
- package/build/executor/cli.js +12 -1
- package/build/executor/completion-contract.js +66 -11
- package/build/executor/completion-supervision.js +99 -0
- package/build/executor/delivery-outcome.js +179 -0
- package/build/executor/deps.js +3 -0
- package/build/executor/env.js +71 -0
- package/build/executor/failure-reason-codes.js +126 -0
- package/build/executor/heartbeat.js +38 -0
- package/build/executor/http-client.js +72 -0
- package/build/executor/integration-job.js +298 -5
- package/build/executor/job-errors.js +36 -6
- package/build/executor/job-runner.js +720 -36
- package/build/executor/likely-tests.js +36 -2
- package/build/executor/merge-job.js +145 -2
- package/build/executor/observation.js +73 -4
- package/build/executor/permissions.js +193 -97
- package/build/executor/preflight.js +14 -3
- package/build/executor/process-containment.js +28 -2
- package/build/executor/prompt-spec.js +50 -5
- package/build/executor/rebase-finalization.js +38 -3
- package/build/executor/reconcile-overlap-job.js +35 -7
- package/build/executor/required-work-ledger.js +236 -81
- package/build/executor/resume-pre-spawn.js +38 -1
- package/build/executor/runner.js +91 -3
- package/build/executor/test-clock.js +8 -0
- package/build/executor/types.js +37 -2
- package/build/executor/worker-cli-launcher.js +259 -0
- package/build/executor/worker-command.js +41 -4
- package/build/executor/worker-database-target.js +91 -0
- package/build/executor/worker-database.js +908 -0
- package/build/executor/worker-finalization.js +106 -0
- package/build/executor/worker-guard-hook.js +203 -0
- package/build/executor/worker-resources.js +47 -0
- package/build/executor/worker-verification-guidance.js +15 -2
- package/build/executor/worktree.js +303 -15
- package/build/executor-launch.js +11 -1
- package/build/generated-assets-doctor.js +9 -1
- package/build/generated-assets-health.js +13 -4
- package/build/generated-assets-legacy.js +39 -0
- package/build/generated-assets-paths.js +12 -1
- package/build/generated-assets-repair.js +4 -1
- package/build/generated-assets-test-support.js +12 -0
- package/build/github-remote.js +33 -0
- package/build/github-sign-in.js +62 -0
- package/build/index.js +1067 -536
- package/build/init.js +39 -1
- package/build/install-bridge-conductor-gate.js +78 -0
- package/build/install-bridge-conductor-setup.js +413 -0
- package/build/install-bridge-conductor.js +1226 -267
- package/build/install-bridge.js +376 -60
- package/build/mcp-install-state.js +8 -2
- package/build/merge-pull-request.js +6 -0
- package/build/merge-workflow-install.js +763 -345
- package/build/phase-result-artifacts.js +75 -20
- package/build/pipeline-orchestrator.js +247 -4
- package/build/pipelines.generated.js +35 -35
- package/build/plan-discovery.js +15 -2
- package/build/plan-execution-ledger.js +306 -17
- package/build/plan-phase-routing.js +48 -13
- package/build/plane/cli.js +105 -51
- package/build/plane/defaults.js +3 -0
- package/build/plane/plane-freshness.js +294 -0
- package/build/plane/schema-compatibility.js +137 -0
- package/build/plane/shutdown-deps.js +38 -0
- package/build/plane/shutdown.js +83 -8
- package/build/plane/terminal-run-cleanup.js +303 -0
- package/build/printed-remedy-commands.js +26 -4
- package/build/readiness-check.js +6 -2
- package/build/readiness-prerequisites.generated.js +532 -0
- package/build/readme.generated.js +1 -1
- package/build/setup-epic.js +400 -30
- package/build/shared-resources.generated.js +5 -0
- package/build/start-tickets.js +28 -15
- package/build/test-command-detection.js +247 -0
- package/build/test-command-validation.js +73 -0
- package/build/testing-config-client.js +150 -0
- package/build/ticket-delivery-artifacts.js +258 -0
- package/build/ticket-delivery-enrollment.js +306 -0
- package/build/ticket-delivery.js +205 -0
- package/build/ticket-review-summary.js +14 -0
- package/build/upgrade-cli.js +13 -0
- package/build/v2-readiness-requirements.js +552 -0
- package/build/version.generated.js +3 -3
- package/build/worker-guard-hook-bin.js +2 -2
- package/build/worktree-core.js +22 -7
- package/docs/BRIDGE-DIRECTORY-README.md +48 -0
- package/docs/EPIC-IMPLEMENTER-INSTALL.md +12 -867
- package/docs/EPIC-IMPLEMENTER.md +104 -63
- package/docs/epic-implementer/getting-started.md +131 -0
- package/docs/epic-implementer/self-hosting.md +133 -0
- package/package.json +3 -3
- package/pipelines/idea-to-ticket.json +1 -1
- package/pipelines/implement-ticket.json +8 -8
- package/pipelines/review-ticket.json +2 -2
- package/design-assets/favicon/favicon-32x32.png +0 -0
- package/design-assets/just-logo-rough-draft.png +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mergeReadinessPrerequisites, formatReadinessPrerequisites } from "./readiness-prerequisites.js";
|
|
1
2
|
/**
|
|
2
3
|
* The consolidated ADVISORY epic-implementer readiness gate (BAPI-1055, AC-6/AC-7).
|
|
3
4
|
*
|
|
@@ -28,7 +29,8 @@
|
|
|
28
29
|
* are all failures, not passes.
|
|
29
30
|
*
|
|
30
31
|
* **Containment.** The authoritative refusals are unchanged and live elsewhere:
|
|
31
|
-
* `V2_READINESS_REQUIREMENTS` in `
|
|
32
|
+
* `V2_READINESS_REQUIREMENTS` (declared in `v2-readiness-requirements.ts` since
|
|
33
|
+
* BAPI-1452 and consumed by `drive-epic.ts`) remains the SOLE hard
|
|
32
34
|
* route-selection predicate, and the server-side admission (A1b.1/A1b.2) remains
|
|
33
35
|
* the sole hard refusal. `plane up`'s own blocking preflight still refuses a
|
|
34
36
|
* bring-up on its own terms. This gate contributes to none of those decisions:
|
|
@@ -61,7 +63,8 @@
|
|
|
61
63
|
*
|
|
62
64
|
* **What replaced it.** BAPI-1136 de-personalized the merge: it now authenticates
|
|
63
65
|
* with one explicitly configured, customer-held credential
|
|
64
|
-
* (`
|
|
66
|
+
* (`GITHUB_MERGE_TOKEN` — BAPI-1452's canonical local key, formerly
|
|
67
|
+
* `BAPI_LOCAL_MERGE_TOKEN` — or `BAPI_MACHINE_MERGE_TOKEN`) and never with the
|
|
65
68
|
* ambient session. So the second check was RE-POINTED, not removed —
|
|
66
69
|
* `conductor.gh-cli` still asks whether `gh` exists, and
|
|
67
70
|
* `conductor.merge-credential` asks whether a credential is configured. Removing
|
|
@@ -92,17 +95,22 @@
|
|
|
92
95
|
* unrelated additive change look like a breaking one here, or worse, hide a real
|
|
93
96
|
* one.
|
|
94
97
|
*/
|
|
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";
|
|
98
|
+
import { CONSUMER_GUIDE_POINTER, 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";
|
|
96
99
|
import { mapConductorDoctorReportToReadinessChecks, } from "./doctor.js";
|
|
97
100
|
import { mapInstallStatusChecksToReadinessChecks, } from "../install-doctor.js";
|
|
98
101
|
import { mapPlanePreflightToReadinessChecks, } from "../plane/preflight.js";
|
|
99
102
|
import { runConductorInstallDoctor, } from "./install-doctor.js";
|
|
100
103
|
import { BRING_UP_SECTION_LABELS, describeGateProfile, describeGateProfileWarning, effectiveReviewModeIsRepositoryPolicy, EPIC_BRANCH_CI_SECTION_IDS, epicBranchCiSections, presentRequiredChecks, quotedSubject, } from "./readiness-sections.js";
|
|
101
104
|
import { classifyBringUpFacts } from "./bring-up-facts.js";
|
|
105
|
+
import { projectReadinessRowBlocking, providerRepositoryCheck, resolvedMergeModeCheck, } from "../v2-readiness-requirements.js";
|
|
102
106
|
import { CI_WORKFLOW_PATH, REVIEW_WORKFLOW_PATH, ciWorkflowRemedy, repositoryWorkflowRemedy, } from "./repository-workflow-remedy.js";
|
|
103
107
|
import { classifyMergeExecutionEligibility, MERGE_EXECUTION_INELIGIBLE_REMEDIATION, MERGE_EXECUTION_UNVERIFIABLE_REMEDIATION, } from "./merge-execution-eligibility.js";
|
|
104
108
|
import { createReadinessCheck, createReadinessCheckSafely, summarizeReadinessChecks, validateReadinessChecks, } from "../readiness-check.js";
|
|
105
109
|
import { ADMISSION_EVIDENCE_SCHEMA_VERSION, gateProfileWaivesCiWorkflow, } from "./bridge-api-client.js";
|
|
110
|
+
import { ESCALATION_REACHABILITY_REMEDY, emailTransportDetailForChannel, isFailedEmailTransportDiagnostic, resolveEffectiveEscalationChannel, } from "../escalation-reachability.js";
|
|
111
|
+
// BAPI-1490 — the reachability row lives in a neutral module so drive-epic can build it
|
|
112
|
+
// without referencing this gate; it is re-exported here for gate consumers.
|
|
113
|
+
export { ESCALATION_REACHABILITY_CHECK_ID, buildEscalationReachabilityCheck, } from "../escalation-reachability.js";
|
|
106
114
|
/** This report's own schema version. See the module docstring. */
|
|
107
115
|
export const CONDUCTOR_READINESS_GATE_SCHEMA_VERSION = 1;
|
|
108
116
|
/** Fixed statement carried in every report, so no consumer can mistake it for a gate. */
|
|
@@ -115,11 +123,18 @@ export const CONDUCTOR_READINESS_ADVISORY_NOTICE = "Advisory only. This report n
|
|
|
115
123
|
// ---------------------------------------------------------------------------
|
|
116
124
|
/** The complete server-side prerequisite set, in render order. */
|
|
117
125
|
export const SERVER_READINESS_DESCRIPTORS = [
|
|
126
|
+
// BAPI-1452 — the launch summary order: the Setup-page unattended posture
|
|
127
|
+
// first, then the supporting merge facts. The hard-coded `auto-merge` and
|
|
128
|
+
// `merge-approval` rows are RETIRED: an unattended run's own merges are
|
|
129
|
+
// authorized by the run and this one switch, not by the legacy repository
|
|
130
|
+
// merge flags, so reporting those flags here described a decision they no
|
|
131
|
+
// longer make.
|
|
132
|
+
{ id: "unattended-posture", label: "Unattended posture (Setup page)" },
|
|
133
|
+
{ id: "resolved-merge-mode", label: "Resolved merge mode" },
|
|
134
|
+
{ id: "provider-repository", label: "Provider repository" },
|
|
118
135
|
{ id: "supervisor-setup", label: "Supervisor setup" },
|
|
119
136
|
{ id: "supervisor-config", label: "Supervisor configuration" },
|
|
120
137
|
{ id: "supervisor-required-checks", label: "Supervisor required CI checks" },
|
|
121
|
-
{ id: "auto-merge", label: "Auto-merge posture" },
|
|
122
|
-
{ id: "merge-approval", label: "Merge approval configuration" },
|
|
123
138
|
{ id: "review-policy", label: "Review policy" },
|
|
124
139
|
{ id: "github-credentials", label: "GitHub App credentials" },
|
|
125
140
|
{ id: "github-actions-permission", label: "GitHub App actions permission" },
|
|
@@ -149,7 +164,6 @@ export const SERVER_READINESS_DESCRIPTORS = [
|
|
|
149
164
|
// BAPI-1102 — the two unattended repository prerequisites plus the aggregate the
|
|
150
165
|
// third unattended refusal reads. Noun phrases, like every row above: these
|
|
151
166
|
// render as list items under a source heading, not as sentences.
|
|
152
|
-
{ id: "unattended-consent", label: "Repository unattended consent" },
|
|
153
167
|
{ id: "unattended-notify-default", label: "Escalation channel (webhook or email)" },
|
|
154
168
|
{ id: "repository-workflow-readiness", label: "Repository workflow readiness" },
|
|
155
169
|
];
|
|
@@ -176,33 +190,32 @@ const SERVER_FAILURE_COPY = {
|
|
|
176
190
|
const SERVER_REMEDIATIONS = {
|
|
177
191
|
supervisorSetup: `run \`${PRINTED_CONDUCTOR_INSTALL_COMMAND}\` (or \`setup-epic\`) for this repository to write the project-default supervisor posture. ${PRINTED_CHECK_NAME_INSTRUCTION}`,
|
|
178
192
|
supervisorConfig: `run \`${PRINTED_CONDUCTOR_INSTALL_COMMAND}\` for this repository to write the project-default supervisor configuration. ${PRINTED_CHECK_NAME_INSTRUCTION}`,
|
|
179
|
-
mergeApproval: "set the merge-approval requirement explicitly in the supervisor configuration rather than leaving it unset.",
|
|
180
193
|
reviewPolicyMissing: "set the repository review-policy default so the merge path and the code_review gate read the same signal.",
|
|
181
194
|
reviewPolicyDivergent: "align the repository review-policy default with the done gate's review_state condition, or confirm the difference is intended.",
|
|
182
195
|
githubCredentials: `connect GitHub from the Bridge setup UI or run \`${PRINTED_CONNECT_GITHUB_COMMAND}\` so owner, repository id, and installation id all resolve.`,
|
|
183
196
|
actionsProbeFailed: "re-run once credentials resolve; the permission could not be checked, which is not the same as confirmed-missing.",
|
|
184
|
-
actionsMissing: "grant the GitHub App `actions: write`; without it the
|
|
197
|
+
actionsMissing: "grant the GitHub App `actions: write`; without it the workflow rerun lane fails open on 403.",
|
|
185
198
|
reconcilerUnreadable: "retry once the Bridge API is reachable; reconciler liveness is unknown here, not confirmed stale.",
|
|
186
199
|
// BAPI-1360: the frozen `epic-tick` command is never named. The reconciler runs
|
|
187
200
|
// inside the worker, so the first check is that the worker is up, then the run's
|
|
188
201
|
// own status.
|
|
189
202
|
reconcilerStale: "check the worker first (`server.worker-present`), then inspect the stalled run with `epic-implementer epic-status`.",
|
|
190
203
|
executorUnreadable: "retry once the Bridge API is reachable; no executor readiness claim is ever made from local state.",
|
|
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}
|
|
193
|
-
executorStale:
|
|
194
|
-
reviewWorkflowUnknown:
|
|
195
|
-
reviewWorkflowIncomplete:
|
|
196
|
-
ciWorkflowUnknown:
|
|
204
|
+
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} See ${CONSUMER_GUIDE_POINTER}.`,
|
|
205
|
+
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} See ${CONSUMER_GUIDE_POINTER}.`,
|
|
206
|
+
executorStale: `restart the executor service; it was observed once and has gone quiet past the server's staleness threshold. See ${CONSUMER_GUIDE_POINTER}.`,
|
|
207
|
+
reviewWorkflowUnknown: `re-run; the default branch's review workflow could not be evaluated, which is not the same as it being absent (see ${CONSUMER_GUIDE_POINTER}).`,
|
|
208
|
+
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 (see ${CONSUMER_GUIDE_POINTER}).`,
|
|
209
|
+
ciWorkflowUnknown: `re-run; the default branch's Epic Implementer CI workflow could not be evaluated, which is not the same as the guard being absent (see ${CONSUMER_GUIDE_POINTER}).`,
|
|
197
210
|
// BAPI-1359: the missing-guard remedy is built by `ciWorkflowRemedy` (the shared
|
|
198
211
|
// formatter), because its wording depends on WHY the guard is missing — a
|
|
199
212
|
// confirmed Alembic chain, an older server, or an unreadable answer.
|
|
200
|
-
olderServer: "
|
|
213
|
+
olderServer: "retry later; the server did not report this fact, so its state is unknown here, not healthy. If it persists, email info@bridgegpt.ai.",
|
|
201
214
|
// BAPI-1102. Each names the ONE action that clears it, and none of them
|
|
202
215
|
// mentions a webhook URL, a destination, or any configuration value.
|
|
203
216
|
// BAPI-1273: consent defaults ON; the repository owner switches it on the
|
|
204
217
|
// admin panel's Setup page. No MCP or CLI surface ever writes it.
|
|
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,
|
|
218
|
+
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, email info@bridgegpt.ai.",
|
|
206
219
|
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.",
|
|
207
220
|
// BAPI-1359: the `repository-workflow-readiness` remedy is built by
|
|
208
221
|
// `repositoryWorkflowRemedy` (the shared formatter) from the SAME report, so it
|
|
@@ -221,7 +234,9 @@ const SERVER_REMEDIATIONS = {
|
|
|
221
234
|
* and its own threshold values are reported as given, and
|
|
222
235
|
* `V2_READINESS_REQUIREMENTS` is neither imported nor duplicated here — two
|
|
223
236
|
* implementations of one classification drift, and this one is advisory while
|
|
224
|
-
* that one decides a route.
|
|
237
|
+
* that one decides a route. BAPI-1452: the per-row applicability flag comes
|
|
238
|
+
* from that catalog's shared projection (`projectReadinessRowBlocking`), so this
|
|
239
|
+
* report repeats the requirements' answer instead of forming its own.
|
|
225
240
|
*
|
|
226
241
|
* Uncertainty is never healthy. `actions_probe_succeeded === false`, an
|
|
227
242
|
* unreadable liveness read, `executor.ready === null`, and a field an older
|
|
@@ -233,7 +248,7 @@ const SERVER_REMEDIATIONS = {
|
|
|
233
248
|
* Passing `null` (with a `failure` kind) yields the full descriptor set as
|
|
234
249
|
* failures, so an unreachable server loses no prerequisite from the report.
|
|
235
250
|
*/
|
|
236
|
-
export function mapServerReadinessToChecks(readiness, failure = "unreachable") {
|
|
251
|
+
export function mapServerReadinessToChecks(readiness, failure = "unreachable", callerChannel) {
|
|
237
252
|
if (readiness === null) {
|
|
238
253
|
const copy = SERVER_FAILURE_COPY[failure];
|
|
239
254
|
return SERVER_READINESS_DESCRIPTORS.map(({ id, label }) => createReadinessCheck({
|
|
@@ -282,21 +297,17 @@ export function mapServerReadinessToChecks(readiness, failure = "unreachable") {
|
|
|
282
297
|
? {}
|
|
283
298
|
: { remediation: requiredChecks.remediation }),
|
|
284
299
|
});
|
|
285
|
-
//
|
|
286
|
-
//
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
: {
|
|
295
|
-
id: "merge-approval",
|
|
296
|
-
status: "warn",
|
|
297
|
-
detail: "the merge-approval requirement is unset, so the merge path falls back to its default",
|
|
298
|
-
remediation: SERVER_REMEDIATIONS.mergeApproval,
|
|
300
|
+
// BAPI-1452 — the retired `auto-merge` / `merge-approval` rows used to sit
|
|
301
|
+
// here. The supporting merge facts come from the shared builders, so this
|
|
302
|
+
// report and drive-epic's launch summary word them identically.
|
|
303
|
+
for (const fact of [resolvedMergeModeCheck(readiness), providerRepositoryCheck(readiness)]) {
|
|
304
|
+
outcomes.push({
|
|
305
|
+
id: fact.id.slice("server.".length),
|
|
306
|
+
status: fact.status,
|
|
307
|
+
detail: fact.detail ?? fact.label,
|
|
308
|
+
...(fact.remediation !== undefined ? { remediation: fact.remediation } : {}),
|
|
299
309
|
});
|
|
310
|
+
}
|
|
300
311
|
outcomes.push(reviewPolicyOutcome(readiness));
|
|
301
312
|
outcomes.push(g.credentials_complete
|
|
302
313
|
? {
|
|
@@ -359,25 +370,47 @@ export function mapServerReadinessToChecks(readiness, failure = "unreachable") {
|
|
|
359
370
|
});
|
|
360
371
|
// BAPI-1260 — appended ONLY when the server reported the block, so every
|
|
361
372
|
// pre-BAPI-1260 report keeps exactly the descriptor set above.
|
|
373
|
+
//
|
|
374
|
+
// BAPI-1452 — the row flags come from the SHARED requirement projection,
|
|
375
|
+
// never from this report's own judgement: a row is flagged exactly when an
|
|
376
|
+
// applicable requirement governs it. This report still decides nothing.
|
|
362
377
|
return [
|
|
363
|
-
...described,
|
|
378
|
+
...projectReadinessRowBlocking(described),
|
|
364
379
|
...admissionEvidenceChecks(readiness),
|
|
365
380
|
// BAPI-1390 — appended ONLY when the server reported a gate profile.
|
|
366
381
|
...gateProfileChecks(readiness),
|
|
367
382
|
// BAPI-1405 — always three warn-only epic-branch CI rows. An older server
|
|
368
|
-
// that omits the block renders them as "Unreported
|
|
383
|
+
// that omits the block renders them as "Unreported by the server",
|
|
369
384
|
// never as "no warning".
|
|
370
385
|
...epicBranchCiChecks(readiness),
|
|
371
|
-
...genericDiagnosticChecks(readiness),
|
|
386
|
+
...genericDiagnosticChecks(readiness, callerChannel),
|
|
372
387
|
];
|
|
373
388
|
}
|
|
374
|
-
function genericDiagnosticChecks(readiness) {
|
|
389
|
+
function genericDiagnosticChecks(readiness, callerChannel) {
|
|
390
|
+
// BAPI-1495 — the same resolver the reachability row uses, so both agree.
|
|
391
|
+
const channel = resolveEffectiveEscalationChannel(readiness, callerChannel);
|
|
375
392
|
return (readiness.diagnostics ?? []).map((diagnostic, index) => {
|
|
376
393
|
const inapplicable = diagnostic.applicability === "inapplicable";
|
|
377
394
|
const declared = diagnostic.severity !== null && diagnostic.applicability !== null;
|
|
378
395
|
const satisfied = declared && diagnostic.verdict === "passed" && diagnostic.applicability === "applicable";
|
|
379
396
|
const advisory = diagnostic.severity === "advisory";
|
|
380
|
-
|
|
397
|
+
// BAPI-1490 — a known failed email transport is a warning with fixed copy,
|
|
398
|
+
// whatever the diagnostic's severity: it reports that no person is told, and
|
|
399
|
+
// this report never blocks.
|
|
400
|
+
const transportNotLive = isFailedEmailTransportDiagnostic(diagnostic);
|
|
401
|
+
const status = transportNotLive
|
|
402
|
+
? "warn"
|
|
403
|
+
: !declared ? "fail" : inapplicable ? "skip" : satisfied ? "pass" : advisory ? "warn" : "fail";
|
|
404
|
+
if (transportNotLive) {
|
|
405
|
+
return createReadinessCheckSafely({
|
|
406
|
+
id: `server.diagnostic-${index + 1}-${diagnostic.id.replace(/[_.]/g, "-")}`,
|
|
407
|
+
source: "server",
|
|
408
|
+
label: `Server diagnostic ${diagnostic.id}`,
|
|
409
|
+
status,
|
|
410
|
+
detail: emailTransportDetailForChannel(channel),
|
|
411
|
+
remediation: ESCALATION_REACHABILITY_REMEDY,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
381
414
|
return createReadinessCheckSafely({
|
|
382
415
|
id: `server.diagnostic-${index + 1}-${diagnostic.id.replace(/[_.]/g, "-")}`,
|
|
383
416
|
source: "server",
|
|
@@ -404,9 +437,9 @@ const ADMISSION_EVIDENCE_LABELS = {
|
|
|
404
437
|
};
|
|
405
438
|
/** Who acts, in fixed prose. Never a value from the payload. */
|
|
406
439
|
const ADMISSION_OWNER_REMEDIATION = {
|
|
407
|
-
operator: "the operator re-runs guided setup (`install conductor
|
|
440
|
+
operator: "the operator re-runs guided setup (`install conductor`) and re-observes.",
|
|
408
441
|
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.",
|
|
409
|
-
bridge_operations: "
|
|
442
|
+
bridge_operations: "email info@bridgegpt.ai; this is not something an operator command can change.",
|
|
410
443
|
};
|
|
411
444
|
/** The retired and current Bridge CI gate names, spelled once for the fixed remedies. */
|
|
412
445
|
const RETIRED_GATE_NAME = "conductor-ci / gate";
|
|
@@ -462,11 +495,11 @@ function gateProfileReasonRemedy(reason, verdict, subject) {
|
|
|
462
495
|
case "provider_evidence_unavailable":
|
|
463
496
|
return (`Bridge could not read the provider's check history for ${quotedSubject(subject, "a gate-profile check")} ` +
|
|
464
497
|
`(${PROVIDER_EVIDENCE_WINDOW}), so it is unknown, not absent. Re-run readiness; if it persists, ` +
|
|
465
|
-
"
|
|
498
|
+
"email info@bridgegpt.ai.");
|
|
466
499
|
case "local_command_unqualified":
|
|
467
500
|
return verdict === "unable_to_verify"
|
|
468
501
|
? `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,
|
|
502
|
+
"be read, so it is unknown, not qualified. Re-run readiness; if it persists, email info@bridgegpt.ai."
|
|
470
503
|
: `the local gate command ${quotedSubject(subject, "in the gate profile")} is not qualified for the current ` +
|
|
471
504
|
"gate-profile revision (unqualified or stale). Qualify it with " +
|
|
472
505
|
`\`${PRINTED_QUALIFY_GATE_COMMAND}\`, or have a repository administrator ` +
|
|
@@ -516,7 +549,7 @@ export function gateProfileChecks(readiness) {
|
|
|
516
549
|
status: "fail",
|
|
517
550
|
detail: `${profile.read_failed ? "the stored gate profile could not be read" : "the stored gate profile is malformed"} — ${detail}`,
|
|
518
551
|
remediation: profile.read_failed
|
|
519
|
-
? "re-run readiness; if it persists,
|
|
552
|
+
? "re-run readiness; if it persists, email info@bridgegpt.ai."
|
|
520
553
|
: "a repository administrator re-saves the gate profile; setup never grants it.",
|
|
521
554
|
});
|
|
522
555
|
}
|
|
@@ -574,7 +607,7 @@ export function gateProfileChecks(readiness) {
|
|
|
574
607
|
* these rows never fail, never hold a launch, and feed no verdict.
|
|
575
608
|
*
|
|
576
609
|
* Unlike `gateProfileChecks`, an absent or `null` block (older server) still
|
|
577
|
-
* yields the three rows, each `warn` with "Unreported
|
|
610
|
+
* yields the three rows, each `warn` with "Unreported by the server" and
|
|
578
611
|
* the upgrade remediation. Emitting nothing would read as "no warning", which
|
|
579
612
|
* is the wrong answer: the server simply did not say. Omitted and `null` render
|
|
580
613
|
* identically. The install doctor's own copies of these sections are filtered
|
|
@@ -720,7 +753,7 @@ function unattendedOutcomes(readiness) {
|
|
|
720
753
|
const u = readiness.unattended;
|
|
721
754
|
if (u === null) {
|
|
722
755
|
return [
|
|
723
|
-
"unattended-
|
|
756
|
+
"unattended-posture",
|
|
724
757
|
"unattended-notify-default",
|
|
725
758
|
"repository-workflow-readiness",
|
|
726
759
|
].map((id) => ({
|
|
@@ -733,12 +766,12 @@ function unattendedOutcomes(readiness) {
|
|
|
733
766
|
return [
|
|
734
767
|
u.conductor_allowed
|
|
735
768
|
? {
|
|
736
|
-
id: "unattended-
|
|
769
|
+
id: "unattended-posture",
|
|
737
770
|
status: "pass",
|
|
738
771
|
detail: "this repository has consented to unattended Epic Implementer runs",
|
|
739
772
|
}
|
|
740
773
|
: {
|
|
741
|
-
id: "unattended-
|
|
774
|
+
id: "unattended-posture",
|
|
742
775
|
status: "warn",
|
|
743
776
|
detail: "unattended runs are off for this repository — an unattended run will be refused before it is created",
|
|
744
777
|
remediation: SERVER_REMEDIATIONS.unattendedConsent,
|
|
@@ -760,7 +793,7 @@ function unattendedOutcomes(readiness) {
|
|
|
760
793
|
// reported by the server and is never rendered here.
|
|
761
794
|
id: "unattended-notify-default",
|
|
762
795
|
status: "pass",
|
|
763
|
-
detail: "No webhook
|
|
796
|
+
detail: "No repository webhook default; a run's policy may declare notify.webhook_url, otherwise escalation alerts are emailed to the account's email address",
|
|
764
797
|
}
|
|
765
798
|
: {
|
|
766
799
|
id: "unattended-notify-default",
|
|
@@ -1029,7 +1062,7 @@ function reviewWorkflowOutcome(readiness) {
|
|
|
1029
1062
|
id: "review-workflow",
|
|
1030
1063
|
status: "fail",
|
|
1031
1064
|
detail: "this Bridge API deploy did not report the review-workflow facts",
|
|
1032
|
-
remediation: SERVER_REMEDIATIONS.olderServer
|
|
1065
|
+
remediation: `${SERVER_REMEDIATIONS.olderServer} See ${CONSUMER_GUIDE_POINTER}.`,
|
|
1033
1066
|
};
|
|
1034
1067
|
}
|
|
1035
1068
|
if (!w.probe_succeeded) {
|
|
@@ -1089,7 +1122,7 @@ function ciWorkflowOutcome(readiness) {
|
|
|
1089
1122
|
id: "conductor-ci-workflow",
|
|
1090
1123
|
status: "fail",
|
|
1091
1124
|
detail: "this Bridge API deploy did not report the epic-implementer-ci workflow facts",
|
|
1092
|
-
remediation: SERVER_REMEDIATIONS.olderServer
|
|
1125
|
+
remediation: `${SERVER_REMEDIATIONS.olderServer} See ${CONSUMER_GUIDE_POINTER}.`,
|
|
1093
1126
|
};
|
|
1094
1127
|
}
|
|
1095
1128
|
if (!c.probe_succeeded) {
|
|
@@ -1097,8 +1130,8 @@ function ciWorkflowOutcome(readiness) {
|
|
|
1097
1130
|
id: "conductor-ci-workflow",
|
|
1098
1131
|
status: "fail",
|
|
1099
1132
|
detail: c.workflow_present
|
|
1100
|
-
? "the
|
|
1101
|
-
: "the
|
|
1133
|
+
? "the Epic Implementer CI workflow is present but could not be evaluated"
|
|
1134
|
+
: "the Epic Implementer CI workflow could not be found or read on the default branch",
|
|
1102
1135
|
remediation: SERVER_REMEDIATIONS.ciWorkflowUnknown,
|
|
1103
1136
|
};
|
|
1104
1137
|
}
|
|
@@ -1115,7 +1148,7 @@ function ciWorkflowOutcome(readiness) {
|
|
|
1115
1148
|
return {
|
|
1116
1149
|
id: "conductor-ci-workflow",
|
|
1117
1150
|
status: "pass",
|
|
1118
|
-
detail: "the default branch's
|
|
1151
|
+
detail: "the default branch's Epic Implementer CI workflow runs the migration guard",
|
|
1119
1152
|
};
|
|
1120
1153
|
}
|
|
1121
1154
|
// BAPI-1359 — the guard is conditional on an Alembic chain. Three explicit
|
|
@@ -1133,7 +1166,7 @@ function ciWorkflowOutcome(readiness) {
|
|
|
1133
1166
|
return {
|
|
1134
1167
|
id: "conductor-ci-workflow",
|
|
1135
1168
|
status: "fail",
|
|
1136
|
-
detail: "the Alembic check could not be read, so it is not known whether the
|
|
1169
|
+
detail: "the Alembic check could not be read, so it is not known whether the Epic Implementer CI workflow needs the migration guard",
|
|
1137
1170
|
remediation: ciWorkflowRemedy(c),
|
|
1138
1171
|
};
|
|
1139
1172
|
}
|
|
@@ -1141,10 +1174,10 @@ function ciWorkflowOutcome(readiness) {
|
|
|
1141
1174
|
id: "conductor-ci-workflow",
|
|
1142
1175
|
status: "warn",
|
|
1143
1176
|
detail: c.migration_guard_required === true && c.migration_chain_probe_succeeded === true
|
|
1144
|
-
? "the
|
|
1177
|
+
? "the Epic Implementer CI workflow does not run the migration guard, which this repository requires because it has alembic.ini on its default branch"
|
|
1145
1178
|
: // An older server (no new fields) or a partial report: keep today's
|
|
1146
1179
|
// wording and today's behavior, and claim nothing about alembic.ini.
|
|
1147
|
-
"the
|
|
1180
|
+
"the Epic Implementer CI workflow does not run the migration guard",
|
|
1148
1181
|
remediation: ciWorkflowRemedy(c),
|
|
1149
1182
|
};
|
|
1150
1183
|
}
|
|
@@ -1263,7 +1296,7 @@ export async function collectConductorReadinessGate(deps) {
|
|
|
1263
1296
|
...mapPlanePreflightToReadinessChecks(planeResult, deps.planeNotApplicable, {
|
|
1264
1297
|
...(deps.epicRunId !== undefined ? { epicRunId: deps.epicRunId } : {}),
|
|
1265
1298
|
}),
|
|
1266
|
-
...mapServerReadinessToChecks(installReport?.readiness ?? null, classifyServerFailure(installReport)),
|
|
1299
|
+
...mapServerReadinessToChecks(installReport?.readiness ?? null, classifyServerFailure(installReport), deps.callerChannel),
|
|
1267
1300
|
];
|
|
1268
1301
|
// Validate the ASSEMBLED report, not just the pieces: an injected collector or
|
|
1269
1302
|
// a future adapter could hand over an entry that violates the contract, and a
|
|
@@ -1276,7 +1309,15 @@ export async function collectConductorReadinessGate(deps) {
|
|
|
1276
1309
|
repoName: deps.repoName ?? null,
|
|
1277
1310
|
unavailableSources,
|
|
1278
1311
|
checks: validated,
|
|
1312
|
+
prerequisites: mergeReadinessPrerequisites(validated, installReport?.readiness?.prerequisites),
|
|
1313
|
+
is_production: installReport?.readiness?.is_production,
|
|
1279
1314
|
...(installReport?.readiness?.diagnostics ? { diagnostics: installReport.readiness.diagnostics } : {}),
|
|
1315
|
+
...(installReport?.readiness?.escalation_channel !== undefined
|
|
1316
|
+
? { escalation_channel: installReport.readiness.escalation_channel }
|
|
1317
|
+
: {}),
|
|
1318
|
+
...(installReport?.readiness?.webhook_last_delivery !== undefined
|
|
1319
|
+
? { webhook_last_delivery: installReport.readiness.webhook_last_delivery }
|
|
1320
|
+
: {}),
|
|
1280
1321
|
summary: summarizeReadinessChecks(validated),
|
|
1281
1322
|
};
|
|
1282
1323
|
}
|
|
@@ -1354,35 +1395,6 @@ function readinessTag(status) {
|
|
|
1354
1395
|
* remediation — so neither rendering can show an operator a finding the other
|
|
1355
1396
|
* hides. It computes no overall verdict, because there is none to compute.
|
|
1356
1397
|
*/
|
|
1357
|
-
export function formatConductorReadinessGateReport(report) {
|
|
1358
|
-
|
|
1359
|
-
"Conductor readiness (advisory)",
|
|
1360
|
-
"══════════════════════════════",
|
|
1361
|
-
`schema version: ${report.schemaVersion}`,
|
|
1362
|
-
`repository: ${report.repoName ?? "unresolved"}`,
|
|
1363
|
-
"",
|
|
1364
|
-
];
|
|
1365
|
-
for (const source of ["install", "conductor", "plane", "server"]) {
|
|
1366
|
-
const group = report.checks.filter((check) => check.source === source);
|
|
1367
|
-
if (group.length === 0)
|
|
1368
|
-
continue;
|
|
1369
|
-
lines.push(`[${source}]`);
|
|
1370
|
-
for (const check of group) {
|
|
1371
|
-
lines.push(` ${readinessTag(check.status)} ${check.label} (${check.id})`);
|
|
1372
|
-
if (check.detail)
|
|
1373
|
-
lines.push(` ${check.detail}`);
|
|
1374
|
-
if (check.remediation)
|
|
1375
|
-
lines.push(` → ${check.remediation}`);
|
|
1376
|
-
}
|
|
1377
|
-
lines.push("");
|
|
1378
|
-
}
|
|
1379
|
-
const { pass, warn, fail, skip } = report.summary;
|
|
1380
|
-
lines.push(`summary: ${pass} pass, ${warn} warn, ${fail} fail, ${skip} skip`);
|
|
1381
|
-
if (report.unavailableSources.length > 0) {
|
|
1382
|
-
lines.push(`sources that could not be collected: ${report.unavailableSources.join(", ")} ` +
|
|
1383
|
-
"(their prerequisites above are reported as failures, not as passes)");
|
|
1384
|
-
}
|
|
1385
|
-
lines.push("");
|
|
1386
|
-
lines.push(report.advisory);
|
|
1387
|
-
return lines.join("\n");
|
|
1398
|
+
export function formatConductorReadinessGateReport(report, options = {}) {
|
|
1399
|
+
return formatReadinessPrerequisites(report.prerequisites ?? mergeReadinessPrerequisites(report.checks), { all: options.all, isProduction: report.is_production }).join('\n');
|
|
1388
1400
|
}
|