@bridge_gpt/mcp-server 0.2.65 → 0.2.67
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 +269 -82
- package/build/agent-capabilities/probes.js +15 -5
- package/build/agent-launchers/claude-executor-adapter.js +55 -34
- package/build/agent-launchers/executor-adapter.js +34 -4
- package/build/agents.generated.js +1 -1
- package/build/bounded-wait.js +67 -10
- package/build/bridge-config.js +104 -0
- package/build/codex-bridge-profile.js +10 -1
- package/build/commands.generated.js +12 -12
- package/build/conductor/bridge-api-client.js +372 -10
- package/build/conductor/bring-up-facts.js +1 -1
- package/build/conductor/doctor.js +3 -3
- package/build/conductor/epic-child-continuation.js +61 -34
- package/build/conductor/epic-implementer-cli.js +34 -6
- package/build/conductor/errors.js +4 -3
- package/build/conductor/guided-repair.js +327 -0
- package/build/conductor/hook-runtime-qualification.js +1 -1
- 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 +46 -9
- package/build/conductor/readiness-sections.js +41 -6
- package/build/conductor/readiness.js +204 -30
- package/build/conductor/recovery-cli.js +703 -20
- package/build/conductor/recovery-operations.js +392 -3
- package/build/conductor/repair-contract.js +112 -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/credential-store.js +5 -4
- package/build/diagnostic-text.js +36 -0
- package/build/docs.generated.js +8 -3
- package/build/doctor.js +70 -10
- package/build/drive-epic-key.js +6 -5
- package/build/drive-epic.js +479 -131
- package/build/epic-coherence-input.js +2 -1
- package/build/epic-implementer-bin.js +30 -26
- package/build/epic-implementer-bundle-cli.js +2 -2
- package/build/epic-launch-diagnostic.js +31 -0
- package/build/epic-launch-profile.js +44 -14
- package/build/executor/cli.js +3 -2
- package/build/executor/completion-contract.js +48 -5
- package/build/executor/completion-supervision.js +9 -0
- package/build/executor/correction-dispatch.js +60 -0
- package/build/executor/env.js +25 -0
- package/build/executor/heartbeat.js +1 -1
- package/build/executor/http-client.js +35 -0
- package/build/executor/integration-job.js +6 -1
- package/build/executor/job-errors.js +38 -4
- package/build/executor/job-log-registry.js +340 -73
- package/build/executor/job-runner.js +485 -74
- package/build/executor/job-types.js +8 -13
- package/build/executor/observation.js +6 -1
- package/build/executor/preflight.js +136 -10
- package/build/executor/prompt-spec.js +113 -9
- package/build/executor/required-work-ledger.js +6 -1
- package/build/executor/resume-pre-spawn.js +2 -0
- package/build/executor/runner.js +31 -2
- package/build/executor/service-unit.js +7 -1
- package/build/executor/stale-artifacts.js +5 -4
- package/build/executor/types.js +1 -0
- package/build/executor/watch-cli.js +35 -8
- package/build/executor/worker-command.js +43 -43
- package/build/executor/worker-finalization.js +161 -43
- package/build/executor/worker-guard-hook.js +152 -12
- package/build/executor/worker-log.js +137 -70
- package/build/executor/worktree-gc.js +30 -6
- package/build/executor/worktree.js +82 -1
- package/build/index.js +482 -660
- package/build/init.js +107 -4
- package/build/install-bridge-conductor.js +8 -6
- package/build/install-bridge.js +287 -23
- package/build/install-doctor.js +172 -30
- package/build/install-reexec.js +69 -7
- package/build/macos-terminal-permission-guidance.js +16 -0
- package/build/model-thresholds-tool.js +185 -0
- package/build/phase-context-git-probe.js +68 -0
- package/build/phase-result-artifacts.js +173 -5
- package/build/pipelines.generated.js +19 -25
- 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 +120 -1
- package/build/plane/member-roster.js +94 -19
- package/build/plane/preflight.js +30 -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 +145 -6
- package/build/plane/supervisor.js +588 -177
- package/build/plane/types.js +54 -2
- package/build/plane/worker-exit-code.js +2 -0
- package/build/printed-remedy-commands.js +37 -0
- package/build/project-root.js +99 -0
- package/build/readiness-check.js +1 -1
- package/build/readme.generated.js +1 -1
- package/build/review-generation.js +22 -0
- package/build/review-tickets.js +156 -13
- package/build/setup-epic.js +224 -127
- 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/setup-status.js +2 -0
- package/build/sfcc/tool-wrapper.js +41 -43
- package/build/sfcc/tools-configured.js +96 -0
- 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.js +332 -79
- package/build/tool-error-envelope.js +61 -0
- package/build/upgrade-cli.js +109 -24
- package/build/version.generated.js +3 -3
- package/build/visual-diff.js +22 -1
- package/build/windows-npm-entry.js +103 -0
- package/build/worker-guard-hook-bin.js +3 -5
- package/build/worker-guard-hook-main.js +47 -0
- package/build/worktree-core.js +136 -9
- package/docs/EPIC-IMPLEMENTER-INSTALL.md +849 -0
- package/docs/EPIC-IMPLEMENTER.md +662 -0
- package/docs/install/mcp-tool-integrations.md +25 -15
- package/docs/install/sfcc-integration.md +21 -8
- package/package.json +1 -1
- package/pipelines/implement-ticket.json +1 -1
- 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
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
* The one exception is {@link inspectWorkflowPresence}, which awaits the
|
|
34
34
|
* READ-ONLY reader seams its caller injects and never opens a path of its own.
|
|
35
35
|
*/
|
|
36
|
+
import { PRINTED_CONNECT_GITHUB_COMMAND } from "../printed-remedy-commands.js";
|
|
36
37
|
import { MANAGED_HOOK_NAMES } from "./git-hooks.js";
|
|
37
38
|
import { MCP_PACKAGE_NAME } from "../mcp-identity.js";
|
|
38
39
|
import { CLAUDE_REVIEW_WORKFLOW_DRIFT_REMEDIATION, summarizeClaudeReviewWorkflowDrift, } from "../claude-review-workflow-drift.js";
|
|
@@ -41,7 +42,7 @@ import { classifyMergeExecutionEligibility, MERGE_EXECUTION_UNVERIFIABLE_REMEDIA
|
|
|
41
42
|
import { MERGE_WORKFLOW_CHILD_MERGE_COMPATIBLE_VERSIONS, MERGE_WORKFLOW_PATH, mergeWorkflowVersionSupportsChildMerge, } from "./merge-execution-contract.js";
|
|
42
43
|
import { classifyDatabaseMigration, classifySingleTransitionAuthority, classifyWorkerPresent, } from "./bring-up-facts.js";
|
|
43
44
|
/** Operator runbook pointer surfaced beside the executor gap. */
|
|
44
|
-
export const CONDUCTOR_OPERATOR_RUNBOOK_POINTER = "docs/
|
|
45
|
+
export const CONDUCTOR_OPERATOR_RUNBOOK_POINTER = "docs/epic-implementer/operator-runbook.md";
|
|
45
46
|
/**
|
|
46
47
|
* Capability-matrix guidance for the executor leg.
|
|
47
48
|
*
|
|
@@ -105,6 +106,22 @@ export const REQUIRED_CHECKS_PRESENTATION_COPY = {
|
|
|
105
106
|
remediation: "restore required-check verification (a readable default branch with static lane " +
|
|
106
107
|
"workflows and a current Bridge API), or correct the required-check configuration.",
|
|
107
108
|
},
|
|
109
|
+
// BAPI-1360 — file- and check-specific copy for the two proven, repository-admin
|
|
110
|
+
// causes of `unverifiable`. Fixed strings over the two hard-coded workflow paths
|
|
111
|
+
// and the two gate names; nothing from the payload is interpolated.
|
|
112
|
+
retiredGate: {
|
|
113
|
+
detail: "Could not verify — a configured required check is the retired gate " +
|
|
114
|
+
`conductor-ci / gate; ${REQUIRED_CHECKS_OPERATOR_HOLD}`,
|
|
115
|
+
remediation: "a repository administrator replaces conductor-ci / gate with epic-implementer-ci / gate in the " +
|
|
116
|
+
"plan's required_checks, the supervisor done-gate checks, and branch protection.",
|
|
117
|
+
},
|
|
118
|
+
laneAbsent: {
|
|
119
|
+
detail: "Could not verify — a lane workflow file is missing from the default branch, so a configured " +
|
|
120
|
+
`required check has no producer; ${REQUIRED_CHECKS_OPERATOR_HOLD}`,
|
|
121
|
+
remediation: "a repository administrator adds the missing lane workflow file(s) — " +
|
|
122
|
+
".github/workflows/epic-implementer-ci.yml and/or .github/workflows/claude-review.yml — " +
|
|
123
|
+
"to the default branch so the configured required checks (such as epic-implementer-ci / gate) have a producer.",
|
|
124
|
+
},
|
|
108
125
|
empty: {
|
|
109
126
|
detail: "Could not verify — the required-check list is EMPTY, which is unsafe: the done gate " +
|
|
110
127
|
`would pass unconditionally; ${REQUIRED_CHECKS_OPERATOR_HOLD}`,
|
|
@@ -155,6 +172,24 @@ export function presentRequiredChecks(supervisor) {
|
|
|
155
172
|
...REQUIRED_CHECKS_PRESENTATION_COPY.unknown_name,
|
|
156
173
|
};
|
|
157
174
|
}
|
|
175
|
+
// BAPI-1360 — optional bounded facts refine the generic unverifiable copy. An
|
|
176
|
+
// old server reports neither, and keeps the wording above. Retired gate ranks
|
|
177
|
+
// first, then a proven-absent lane; anything else stays generic.
|
|
178
|
+
if (supervisor.required_checks_retired_gate_configured === true) {
|
|
179
|
+
return {
|
|
180
|
+
outcome: "unverifiable",
|
|
181
|
+
satisfied: false,
|
|
182
|
+
...REQUIRED_CHECKS_PRESENTATION_COPY.retiredGate,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (supervisor.required_checks_ci_workflow_absent === true ||
|
|
186
|
+
supervisor.required_checks_review_workflow_absent === true) {
|
|
187
|
+
return {
|
|
188
|
+
outcome: "unverifiable",
|
|
189
|
+
satisfied: false,
|
|
190
|
+
...REQUIRED_CHECKS_PRESENTATION_COPY.laneAbsent,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
158
193
|
return {
|
|
159
194
|
outcome: "unverifiable",
|
|
160
195
|
satisfied: false,
|
|
@@ -268,7 +303,7 @@ export function githubCredentialsSection(readiness) {
|
|
|
268
303
|
detail: g.credentials_readable
|
|
269
304
|
? `incomplete: ${missing} unresolved`
|
|
270
305
|
: "no GitHub credential row for this repository",
|
|
271
|
-
remediation:
|
|
306
|
+
remediation: `connect GitHub from the Bridge setup UI or run \`${PRINTED_CONNECT_GITHUB_COMMAND}\`.`,
|
|
272
307
|
};
|
|
273
308
|
}
|
|
274
309
|
export function githubActionsSection(readiness) {
|
|
@@ -396,7 +431,7 @@ export function reconcilerSection(readiness) {
|
|
|
396
431
|
// remediation at all — so a report could name a gap with no next step.
|
|
397
432
|
return {
|
|
398
433
|
id: "reconciler-liveness",
|
|
399
|
-
label: "
|
|
434
|
+
label: "Active-run progress",
|
|
400
435
|
status: "degraded",
|
|
401
436
|
detail: "liveness could not be read",
|
|
402
437
|
remediation: "retry once the Bridge API is reachable; reconciler liveness is unknown here, " +
|
|
@@ -406,19 +441,19 @@ export function reconcilerSection(readiness) {
|
|
|
406
441
|
if (!r.stale) {
|
|
407
442
|
return {
|
|
408
443
|
id: "reconciler-liveness",
|
|
409
|
-
label: "
|
|
444
|
+
label: "Active-run progress",
|
|
410
445
|
status: "ok",
|
|
411
446
|
detail: `last tick ${r.last_tick_age_seconds}s ago across ${r.active_run_count} active run(s)`,
|
|
412
447
|
};
|
|
413
448
|
}
|
|
414
449
|
return {
|
|
415
450
|
id: "reconciler-liveness",
|
|
416
|
-
label: "
|
|
451
|
+
label: "Active-run progress",
|
|
417
452
|
status: "degraded",
|
|
418
453
|
detail: r.last_tick_at === null
|
|
419
454
|
? "no reconciler tick recorded for this repository"
|
|
420
455
|
: `last tick ${r.last_tick_age_seconds}s ago (threshold ${readiness.thresholds.reconciler_stale_after_seconds}s)`,
|
|
421
|
-
remediation: "
|
|
456
|
+
remediation: "check the worker first (`server.worker-present`), then inspect the stalled run with `epic-implementer epic-status`.",
|
|
422
457
|
};
|
|
423
458
|
}
|
|
424
459
|
// ---------------------------------------------------------------------------
|
|
@@ -92,12 +92,14 @@
|
|
|
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, } 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
100
|
import { BRING_UP_SECTION_LABELS, presentRequiredChecks, } 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
105
|
import { ADMISSION_EVIDENCE_SCHEMA_VERSION, } from "./bridge-api-client.js";
|
|
@@ -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,42 @@ 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 [...described, ...admissionEvidenceChecks(readiness)];
|
|
362
|
+
return [...described, ...admissionEvidenceChecks(readiness), ...genericDiagnosticChecks(readiness)];
|
|
363
|
+
}
|
|
364
|
+
function genericDiagnosticChecks(readiness) {
|
|
365
|
+
return (readiness.diagnostics ?? []).map((diagnostic, index) => {
|
|
366
|
+
const inapplicable = diagnostic.applicability === "inapplicable";
|
|
367
|
+
const declared = diagnostic.severity !== null && diagnostic.applicability !== null;
|
|
368
|
+
const satisfied = declared && diagnostic.verdict === "passed" && diagnostic.applicability === "applicable";
|
|
369
|
+
const advisory = diagnostic.severity === "advisory";
|
|
370
|
+
const status = !declared ? "fail" : inapplicable ? "skip" : satisfied ? "pass" : advisory ? "warn" : "fail";
|
|
371
|
+
return createReadinessCheckSafely({
|
|
372
|
+
id: `server.diagnostic-${index + 1}-${diagnostic.id.replace(/[_.]/g, "-")}`,
|
|
373
|
+
source: "server",
|
|
374
|
+
label: `Server diagnostic ${diagnostic.id}`,
|
|
375
|
+
status,
|
|
376
|
+
detail: `severity ${diagnostic.severity ?? "unknown"}; applicability ${diagnostic.applicability ?? "unknown"}; verdict ${diagnostic.verdict}`,
|
|
377
|
+
...(status === "pass" ? {} : {
|
|
378
|
+
remediation: status === "skip"
|
|
379
|
+
? "This diagnostic does not apply to the selected topology."
|
|
380
|
+
: "Restore the diagnostic producer or resolve the reported condition before launch.",
|
|
381
|
+
}),
|
|
382
|
+
});
|
|
383
|
+
});
|
|
342
384
|
}
|
|
343
385
|
/** Human labels for the known BAPI-1260 evidence predicates. */
|
|
344
386
|
const ADMISSION_EVIDENCE_LABELS = {
|
|
@@ -356,6 +398,44 @@ const ADMISSION_OWNER_REMEDIATION = {
|
|
|
356
398
|
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.",
|
|
357
399
|
bridge_operations: "escalate to Bridge operations; this is not something an operator command can change.",
|
|
358
400
|
};
|
|
401
|
+
/** The retired and current Bridge CI gate names, spelled once for the fixed remedies. */
|
|
402
|
+
const RETIRED_GATE_NAME = "conductor-ci / gate";
|
|
403
|
+
const CURRENT_GATE_NAME = "epic-implementer-ci / gate";
|
|
404
|
+
/**
|
|
405
|
+
* Fixed, per-reason remedies (BAPI-1360). Assembled from literals and the two
|
|
406
|
+
* hard-coded workflow paths only: a record's reason picks a template, and nothing
|
|
407
|
+
* from the payload is interpolated. `null` means "no specific remedy — use the
|
|
408
|
+
* owner's generic one".
|
|
409
|
+
*/
|
|
410
|
+
function admissionReasonRemedy(record, readiness) {
|
|
411
|
+
const sup = readiness.supervisor;
|
|
412
|
+
const ciAbsent = sup.required_checks_ci_workflow_absent === true ||
|
|
413
|
+
(readiness.conductor_ci_workflow?.probe_succeeded === true &&
|
|
414
|
+
readiness.conductor_ci_workflow?.workflow_present === false);
|
|
415
|
+
const reviewAbsent = sup.required_checks_review_workflow_absent === true ||
|
|
416
|
+
(readiness.review_workflow?.probe_succeeded === true &&
|
|
417
|
+
readiness.review_workflow?.workflow_present === false);
|
|
418
|
+
switch (record.reason) {
|
|
419
|
+
case "review_workflow_unconfirmed":
|
|
420
|
+
return (`a repository administrator adds ${REVIEW_WORKFLOW_PATH} to the default branch, or repairs it so it ` +
|
|
421
|
+
"emits the run-id: and Reviewed-SHA: markers in its verdict comment; setup never grants it.");
|
|
422
|
+
case "conductor_ci_workflow_unconfirmed":
|
|
423
|
+
return (`a repository administrator adds ${CI_WORKFLOW_PATH} to the default branch, or repairs it (a ` +
|
|
424
|
+
"migration-guard job is required when the repository has alembic.ini); setup never grants it.");
|
|
425
|
+
case "required_checks_lane_workflow_absent": {
|
|
426
|
+
const files = [ciAbsent ? CI_WORKFLOW_PATH : null, reviewAbsent ? REVIEW_WORKFLOW_PATH : null].filter((file) => file !== null);
|
|
427
|
+
const named = files.length > 0 ? files.join(" and ") : `${CI_WORKFLOW_PATH} and ${REVIEW_WORKFLOW_PATH}`;
|
|
428
|
+
return (`a repository administrator adds ${named} to the default branch so the configured required ` +
|
|
429
|
+
`checks (such as ${CURRENT_GATE_NAME}) have a producer; setup never grants it.`);
|
|
430
|
+
}
|
|
431
|
+
case "required_check_retired_gate":
|
|
432
|
+
return (`a repository administrator replaces ${RETIRED_GATE_NAME} with ${CURRENT_GATE_NAME} in the plan's ` +
|
|
433
|
+
"required_checks, in the supervisor done-gate checks, and in branch protection." +
|
|
434
|
+
(ciAbsent ? ` Also add ${CI_WORKFLOW_PATH} to the default branch so ${CURRENT_GATE_NAME} has a producer.` : ""));
|
|
435
|
+
default:
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
359
439
|
/**
|
|
360
440
|
* Project the server's typed launch evidence (BAPI-1260) into `server.*`
|
|
361
441
|
* checks, read-only.
|
|
@@ -384,13 +464,30 @@ export function admissionEvidenceChecks(readiness) {
|
|
|
384
464
|
}),
|
|
385
465
|
];
|
|
386
466
|
}
|
|
387
|
-
return evidence.records.map((record) => admissionEvidenceCheck(record));
|
|
467
|
+
return evidence.records.map((record) => admissionEvidenceCheck(record, readiness));
|
|
388
468
|
}
|
|
389
|
-
function admissionEvidenceCheck(record) {
|
|
469
|
+
function admissionEvidenceCheck(record, readiness) {
|
|
390
470
|
const slug = record.predicate.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
391
471
|
const label = ADMISSION_EVIDENCE_LABELS[record.predicate] ?? "Launch evidence";
|
|
392
472
|
const reason = record.reason ? ` (${record.reason})` : "";
|
|
393
|
-
const remediation =
|
|
473
|
+
const remediation = admissionReasonRemedy(record, readiness) ??
|
|
474
|
+
ADMISSION_OWNER_REMEDIATION[record.next_action_owner ?? "operator"] ??
|
|
475
|
+
ADMISSION_OWNER_REMEDIATION.operator;
|
|
476
|
+
// BAPI-1360: the durable-actor handshake being unsupported is a known, current
|
|
477
|
+
// limitation, not a hold — it renders as a warning that says so. Every other
|
|
478
|
+
// predicate, and every other reason for this one, keeps its existing branch.
|
|
479
|
+
if (record.predicate === "durable_actor" && record.reason === "actor_handshake_unsupported") {
|
|
480
|
+
return createReadinessCheckSafely({
|
|
481
|
+
id: `server.admission-${slug}`,
|
|
482
|
+
source: "server",
|
|
483
|
+
label,
|
|
484
|
+
status: "warn",
|
|
485
|
+
detail: `not yet supported by this deploy${reason} — this does not hold a launch`,
|
|
486
|
+
// Fixed, non-escalating: the owner remedy would tell the operator to escalate
|
|
487
|
+
// for a limitation that blocks nothing.
|
|
488
|
+
remediation: "no action is needed to launch; this warning clears once the deploy supports the durable-actor handshake.",
|
|
489
|
+
});
|
|
490
|
+
}
|
|
394
491
|
switch (record.verdict) {
|
|
395
492
|
case "confirmed":
|
|
396
493
|
return createReadinessCheckSafely({
|
|
@@ -482,7 +579,7 @@ function unattendedOutcomes(readiness) {
|
|
|
482
579
|
? {
|
|
483
580
|
id: "unattended-consent",
|
|
484
581
|
status: "pass",
|
|
485
|
-
detail: "this repository has consented to unattended
|
|
582
|
+
detail: "this repository has consented to unattended Epic Implementer runs",
|
|
486
583
|
}
|
|
487
584
|
: {
|
|
488
585
|
id: "unattended-consent",
|
|
@@ -519,13 +616,21 @@ function unattendedOutcomes(readiness) {
|
|
|
519
616
|
? {
|
|
520
617
|
id: "repository-workflow-readiness",
|
|
521
618
|
status: "pass",
|
|
522
|
-
detail: "the review and
|
|
619
|
+
detail: "the review and epic-implementer-ci workflows are both confirmed",
|
|
523
620
|
}
|
|
524
621
|
: {
|
|
525
622
|
id: "repository-workflow-readiness",
|
|
526
623
|
status: "warn",
|
|
527
|
-
|
|
528
|
-
|
|
624
|
+
// BAPI-1360: name a confirmed-missing file separately from a file that
|
|
625
|
+
// could not be read; the remedy already differs by owner.
|
|
626
|
+
detail: readiness.review_workflow?.probe_succeeded === true &&
|
|
627
|
+
readiness.review_workflow?.workflow_present === false
|
|
628
|
+
? `${REVIEW_WORKFLOW_PATH} is missing from the default branch — an unattended auto-merging run will be refused`
|
|
629
|
+
: readiness.conductor_ci_workflow?.probe_succeeded === true &&
|
|
630
|
+
readiness.conductor_ci_workflow?.workflow_present === false
|
|
631
|
+
? `${CI_WORKFLOW_PATH} is missing from the default branch — an unattended auto-merging run will be refused`
|
|
632
|
+
: "the review and epic-implementer-ci workflows are not both confirmed — an unattended auto-merging run will be refused",
|
|
633
|
+
remediation: repositoryWorkflowRemedy(readiness),
|
|
529
634
|
},
|
|
530
635
|
];
|
|
531
636
|
}
|
|
@@ -656,6 +761,17 @@ function reconcilerOutcome(readiness) {
|
|
|
656
761
|
remediation: SERVER_REMEDIATIONS.reconcilerUnreadable,
|
|
657
762
|
};
|
|
658
763
|
}
|
|
764
|
+
// BAPI-1360 — with no active run there is no progress to judge: the row is an
|
|
765
|
+
// explicit, remedy-free `skip`, not a stale-tick failure about a reconciler that
|
|
766
|
+
// has nothing to reconcile. ONLY a readable response with a strict zero count
|
|
767
|
+
// qualifies; a missing or malformed count keeps the stale/pass reading below.
|
|
768
|
+
if (r.liveness_readable === true && r.active_run_count === 0) {
|
|
769
|
+
return {
|
|
770
|
+
id: "reconciler-liveness",
|
|
771
|
+
status: "skip",
|
|
772
|
+
detail: "no active runs, so there is no run progress to check",
|
|
773
|
+
};
|
|
774
|
+
}
|
|
659
775
|
if (!r.stale) {
|
|
660
776
|
return {
|
|
661
777
|
id: "reconciler-liveness",
|
|
@@ -705,10 +821,21 @@ function executorOutcome(readiness) {
|
|
|
705
821
|
remediation: SERVER_REMEDIATIONS.executorStale,
|
|
706
822
|
};
|
|
707
823
|
}
|
|
824
|
+
// `ready === null`. BAPI-1360: with readable executor AND reconciler liveness and
|
|
825
|
+
// a strict zero active-run count, no executor being present is expected —
|
|
826
|
+
// `drive-epic` starts a run-scoped one — so it is a warning, not a failure.
|
|
827
|
+
if (readiness.reconciler.liveness_readable === true && readiness.reconciler.active_run_count === 0) {
|
|
828
|
+
return {
|
|
829
|
+
id: "executor-readiness",
|
|
830
|
+
status: "warn",
|
|
831
|
+
detail: "no executor was observed; `drive-epic` starts a run-scoped executor at launch, so a durable service is optional",
|
|
832
|
+
remediation: SERVER_REMEDIATIONS.executorNoneObservedNoRuns,
|
|
833
|
+
};
|
|
834
|
+
}
|
|
708
835
|
return {
|
|
709
836
|
id: "executor-readiness",
|
|
710
837
|
status: "fail",
|
|
711
|
-
detail: "
|
|
838
|
+
detail: "no executor was observed while runs are active or their count could not be read",
|
|
712
839
|
remediation: SERVER_REMEDIATIONS.executorNeverSeen,
|
|
713
840
|
};
|
|
714
841
|
}
|
|
@@ -733,6 +860,16 @@ function reviewWorkflowOutcome(readiness) {
|
|
|
733
860
|
remediation: SERVER_REMEDIATIONS.reviewWorkflowUnknown,
|
|
734
861
|
};
|
|
735
862
|
}
|
|
863
|
+
// BAPI-1360 — a SUCCESSFUL probe that found no file is a confirmed absence, kept
|
|
864
|
+
// apart from an unreadable file (Bridge operations) and from missing markers.
|
|
865
|
+
if (w.workflow_present === false) {
|
|
866
|
+
return {
|
|
867
|
+
id: "review-workflow",
|
|
868
|
+
status: "fail",
|
|
869
|
+
detail: `${REVIEW_WORKFLOW_PATH} is missing from the default branch (confirmed)`,
|
|
870
|
+
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.`,
|
|
871
|
+
};
|
|
872
|
+
}
|
|
736
873
|
if (w.emits_run_id && w.emits_reviewed_sha) {
|
|
737
874
|
return {
|
|
738
875
|
id: "review-workflow",
|
|
@@ -759,7 +896,7 @@ function ciWorkflowOutcome(readiness) {
|
|
|
759
896
|
return {
|
|
760
897
|
id: "conductor-ci-workflow",
|
|
761
898
|
status: "fail",
|
|
762
|
-
detail: "this Bridge API deploy did not report the
|
|
899
|
+
detail: "this Bridge API deploy did not report the epic-implementer-ci workflow facts",
|
|
763
900
|
remediation: SERVER_REMEDIATIONS.olderServer,
|
|
764
901
|
};
|
|
765
902
|
}
|
|
@@ -773,18 +910,51 @@ function ciWorkflowOutcome(readiness) {
|
|
|
773
910
|
remediation: SERVER_REMEDIATIONS.ciWorkflowUnknown,
|
|
774
911
|
};
|
|
775
912
|
}
|
|
776
|
-
|
|
777
|
-
|
|
913
|
+
// BAPI-1360 — confirmed absence of the file, not a missing migration guard.
|
|
914
|
+
if (c.workflow_present === false) {
|
|
915
|
+
return {
|
|
916
|
+
id: "conductor-ci-workflow",
|
|
917
|
+
status: "fail",
|
|
918
|
+
detail: `${CI_WORKFLOW_PATH} is missing from the default branch (confirmed)`,
|
|
919
|
+
remediation: `a repository administrator adds ${CI_WORKFLOW_PATH} to the default branch. ${ciWorkflowRemedy(c)}`,
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
if (c.migration_guard_present) {
|
|
923
|
+
return {
|
|
778
924
|
id: "conductor-ci-workflow",
|
|
779
925
|
status: "pass",
|
|
780
926
|
detail: "the default branch's conductor CI workflow runs the migration guard",
|
|
781
|
-
}
|
|
782
|
-
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
// BAPI-1359 — the guard is conditional on an Alembic chain. Three explicit
|
|
930
|
+
// server answers, plus the older-server case where neither field is reported.
|
|
931
|
+
if (c.migration_chain_probe_succeeded === true && c.migration_guard_required === false) {
|
|
932
|
+
return {
|
|
783
933
|
id: "conductor-ci-workflow",
|
|
784
|
-
status: "
|
|
785
|
-
|
|
786
|
-
|
|
934
|
+
status: "pass",
|
|
935
|
+
// States only what was observed. It does not say migrations are safe, or
|
|
936
|
+
// that the repository has none.
|
|
937
|
+
detail: "migration guard not required (no Alembic chain: no alembic.ini on the default branch)",
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
if (c.migration_chain_probe_succeeded === false) {
|
|
941
|
+
return {
|
|
942
|
+
id: "conductor-ci-workflow",
|
|
943
|
+
status: "fail",
|
|
944
|
+
detail: "the Alembic check could not be read, so it is not known whether the conductor CI workflow needs the migration guard",
|
|
945
|
+
remediation: ciWorkflowRemedy(c),
|
|
787
946
|
};
|
|
947
|
+
}
|
|
948
|
+
return {
|
|
949
|
+
id: "conductor-ci-workflow",
|
|
950
|
+
status: "warn",
|
|
951
|
+
detail: c.migration_guard_required === true && c.migration_chain_probe_succeeded === true
|
|
952
|
+
? "the conductor CI workflow does not run the migration guard, which this repository requires because it has alembic.ini on its default branch"
|
|
953
|
+
: // An older server (no new fields) or a partial report: keep today's
|
|
954
|
+
// wording and today's behavior, and claim nothing about alembic.ini.
|
|
955
|
+
"the conductor CI workflow does not run the migration guard",
|
|
956
|
+
remediation: ciWorkflowRemedy(c),
|
|
957
|
+
};
|
|
788
958
|
}
|
|
789
959
|
// ---------------------------------------------------------------------------
|
|
790
960
|
// Install-time section adapter
|
|
@@ -852,7 +1022,7 @@ export function mapInstallSectionsToReadinessChecks(sections) {
|
|
|
852
1022
|
? {}
|
|
853
1023
|
: {
|
|
854
1024
|
remediation: section.remediation ??
|
|
855
|
-
|
|
1025
|
+
`run \`${PRINTED_CONDUCTOR_INSTALL_COMMAND}\` for this repository to re-provision this capability. ${PRINTED_CHECK_NAME_INSTRUCTION}`,
|
|
856
1026
|
}),
|
|
857
1027
|
}));
|
|
858
1028
|
}
|
|
@@ -882,13 +1052,14 @@ export async function collectConductorReadinessGate(deps) {
|
|
|
882
1052
|
}
|
|
883
1053
|
// A plane preflight was requested and could not be run. Not requesting one is
|
|
884
1054
|
// not an unavailability — its prerequisites are still reported as unknowns.
|
|
885
|
-
if (deps.runPlanePreflight && planeResult === null)
|
|
1055
|
+
if (deps.runPlanePreflight && deps.planeNotApplicable === undefined && planeResult === null) {
|
|
886
1056
|
unavailableSources.push("plane");
|
|
1057
|
+
}
|
|
887
1058
|
const checks = [
|
|
888
1059
|
...mapInstallStatusChecksToReadinessChecks((installReport?.legacyInstallChecks ?? null), installReport === null),
|
|
889
1060
|
...mapConductorDoctorReportToReadinessChecks(installReport?.legacyConductor ?? null),
|
|
890
1061
|
...mapInstallSectionsToReadinessChecks(installReport?.sections ?? null),
|
|
891
|
-
...mapPlanePreflightToReadinessChecks(planeResult),
|
|
1062
|
+
...mapPlanePreflightToReadinessChecks(planeResult, deps.planeNotApplicable),
|
|
892
1063
|
...mapServerReadinessToChecks(installReport?.readiness ?? null, classifyServerFailure(installReport)),
|
|
893
1064
|
];
|
|
894
1065
|
// Validate the ASSEMBLED report, not just the pieces: an injected collector or
|
|
@@ -902,6 +1073,7 @@ export async function collectConductorReadinessGate(deps) {
|
|
|
902
1073
|
repoName: deps.repoName ?? null,
|
|
903
1074
|
unavailableSources,
|
|
904
1075
|
checks: validated,
|
|
1076
|
+
...(installReport?.readiness?.diagnostics ? { diagnostics: installReport.readiness.diagnostics } : {}),
|
|
905
1077
|
summary: summarizeReadinessChecks(validated),
|
|
906
1078
|
};
|
|
907
1079
|
}
|
|
@@ -924,6 +1096,8 @@ async function runInstallDoctorSafely(deps) {
|
|
|
924
1096
|
}
|
|
925
1097
|
/** Run the plane preflight, containing any throw. */
|
|
926
1098
|
async function runPlaneSafely(deps) {
|
|
1099
|
+
if (deps.planeNotApplicable !== undefined)
|
|
1100
|
+
return null;
|
|
927
1101
|
if (!deps.runPlanePreflight)
|
|
928
1102
|
return null;
|
|
929
1103
|
try {
|