@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.
Files changed (132) hide show
  1. package/README.md +269 -82
  2. package/build/agent-capabilities/probes.js +15 -5
  3. package/build/agent-launchers/claude-executor-adapter.js +55 -34
  4. package/build/agent-launchers/executor-adapter.js +34 -4
  5. package/build/agents.generated.js +1 -1
  6. package/build/bounded-wait.js +67 -10
  7. package/build/bridge-config.js +104 -0
  8. package/build/codex-bridge-profile.js +10 -1
  9. package/build/commands.generated.js +12 -12
  10. package/build/conductor/bridge-api-client.js +372 -10
  11. package/build/conductor/bring-up-facts.js +1 -1
  12. package/build/conductor/doctor.js +3 -3
  13. package/build/conductor/epic-child-continuation.js +61 -34
  14. package/build/conductor/epic-implementer-cli.js +34 -6
  15. package/build/conductor/errors.js +4 -3
  16. package/build/conductor/guided-repair.js +327 -0
  17. package/build/conductor/hook-runtime-qualification.js +1 -1
  18. package/build/conductor/merge-execution-contract.js +27 -8
  19. package/build/conductor/merge-execution-eligibility.js +3 -2
  20. package/build/conductor/pr-ci-producer.js +1 -1
  21. package/build/conductor/readiness-cli.js +46 -9
  22. package/build/conductor/readiness-sections.js +41 -6
  23. package/build/conductor/readiness.js +204 -30
  24. package/build/conductor/recovery-cli.js +703 -20
  25. package/build/conductor/recovery-operations.js +392 -3
  26. package/build/conductor/repair-contract.js +112 -3
  27. package/build/conductor/repository-workflow-remedy.js +125 -0
  28. package/build/conductor/tools.js +2 -2
  29. package/build/conductor-bundle-artifacts.js +8 -0
  30. package/build/credential-store.js +5 -4
  31. package/build/diagnostic-text.js +36 -0
  32. package/build/docs.generated.js +8 -3
  33. package/build/doctor.js +70 -10
  34. package/build/drive-epic-key.js +6 -5
  35. package/build/drive-epic.js +479 -131
  36. package/build/epic-coherence-input.js +2 -1
  37. package/build/epic-implementer-bin.js +30 -26
  38. package/build/epic-implementer-bundle-cli.js +2 -2
  39. package/build/epic-launch-diagnostic.js +31 -0
  40. package/build/epic-launch-profile.js +44 -14
  41. package/build/executor/cli.js +3 -2
  42. package/build/executor/completion-contract.js +48 -5
  43. package/build/executor/completion-supervision.js +9 -0
  44. package/build/executor/correction-dispatch.js +60 -0
  45. package/build/executor/env.js +25 -0
  46. package/build/executor/heartbeat.js +1 -1
  47. package/build/executor/http-client.js +35 -0
  48. package/build/executor/integration-job.js +6 -1
  49. package/build/executor/job-errors.js +38 -4
  50. package/build/executor/job-log-registry.js +340 -73
  51. package/build/executor/job-runner.js +485 -74
  52. package/build/executor/job-types.js +8 -13
  53. package/build/executor/observation.js +6 -1
  54. package/build/executor/preflight.js +136 -10
  55. package/build/executor/prompt-spec.js +113 -9
  56. package/build/executor/required-work-ledger.js +6 -1
  57. package/build/executor/resume-pre-spawn.js +2 -0
  58. package/build/executor/runner.js +31 -2
  59. package/build/executor/service-unit.js +7 -1
  60. package/build/executor/stale-artifacts.js +5 -4
  61. package/build/executor/types.js +1 -0
  62. package/build/executor/watch-cli.js +35 -8
  63. package/build/executor/worker-command.js +43 -43
  64. package/build/executor/worker-finalization.js +161 -43
  65. package/build/executor/worker-guard-hook.js +152 -12
  66. package/build/executor/worker-log.js +137 -70
  67. package/build/executor/worktree-gc.js +30 -6
  68. package/build/executor/worktree.js +82 -1
  69. package/build/index.js +482 -660
  70. package/build/init.js +107 -4
  71. package/build/install-bridge-conductor.js +8 -6
  72. package/build/install-bridge.js +287 -23
  73. package/build/install-doctor.js +172 -30
  74. package/build/install-reexec.js +69 -7
  75. package/build/macos-terminal-permission-guidance.js +16 -0
  76. package/build/model-thresholds-tool.js +185 -0
  77. package/build/phase-context-git-probe.js +68 -0
  78. package/build/phase-result-artifacts.js +173 -5
  79. package/build/pipelines.generated.js +19 -25
  80. package/build/plane/alembic-head.js +12 -3
  81. package/build/plane/base-worktree-guard.js +147 -0
  82. package/build/plane/cli.js +217 -17
  83. package/build/plane/code-snapshot.js +468 -0
  84. package/build/plane/defaults.js +48 -3
  85. package/build/plane/endpoint.js +41 -0
  86. package/build/plane/manifest.js +120 -1
  87. package/build/plane/member-roster.js +94 -19
  88. package/build/plane/preflight.js +30 -2
  89. package/build/plane/process-cwd.js +67 -0
  90. package/build/plane/restart.js +542 -0
  91. package/build/plane/shutdown.js +31 -0
  92. package/build/plane/status.js +145 -6
  93. package/build/plane/supervisor.js +588 -177
  94. package/build/plane/types.js +54 -2
  95. package/build/plane/worker-exit-code.js +2 -0
  96. package/build/printed-remedy-commands.js +37 -0
  97. package/build/project-root.js +99 -0
  98. package/build/readiness-check.js +1 -1
  99. package/build/readme.generated.js +1 -1
  100. package/build/review-generation.js +22 -0
  101. package/build/review-tickets.js +156 -13
  102. package/build/setup-epic.js +224 -127
  103. package/build/sfcc/config.js +9 -0
  104. package/build/sfcc/credentials.js +100 -38
  105. package/build/sfcc/hostname.js +67 -0
  106. package/build/sfcc/permissions.js +17 -13
  107. package/build/sfcc/register.js +1 -1
  108. package/build/sfcc/setup-status.js +2 -0
  109. package/build/sfcc/tool-wrapper.js +41 -43
  110. package/build/sfcc/tools-configured.js +96 -0
  111. package/build/sfcc/write-grants.js +17 -11
  112. package/build/sfcc/write-result.js +4 -3
  113. package/build/sfcc/writes-site-preference.js +20 -8
  114. package/build/start-tickets.js +332 -79
  115. package/build/tool-error-envelope.js +61 -0
  116. package/build/upgrade-cli.js +109 -24
  117. package/build/version.generated.js +3 -3
  118. package/build/visual-diff.js +22 -1
  119. package/build/windows-npm-entry.js +103 -0
  120. package/build/worker-guard-hook-bin.js +3 -5
  121. package/build/worker-guard-hook-main.js +47 -0
  122. package/build/worktree-core.js +136 -9
  123. package/docs/EPIC-IMPLEMENTER-INSTALL.md +849 -0
  124. package/docs/EPIC-IMPLEMENTER.md +662 -0
  125. package/docs/install/mcp-tool-integrations.md +25 -15
  126. package/docs/install/sfcc-integration.md +21 -8
  127. package/package.json +1 -1
  128. package/pipelines/implement-ticket.json +1 -1
  129. package/pipelines/plan-epic.json +0 -5
  130. package/pipelines/review-ticket.json +1 -1
  131. package/docs/CONDUCTOR.md +0 -605
  132. 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/claude/epic-implementer-v2-operator-runbook.md";
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: "connect GitHub from the Bridge setup UI (`install-bridge connect-github`).",
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: "Reconciler tick liveness",
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: "Reconciler tick liveness",
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: "Reconciler tick liveness",
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: "start the reconciler (`epic-implementer epic-tick` schedule) for this repository.",
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
- { id: "reconciler-liveness", label: "Reconciler tick liveness" },
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: "Verified notify webhook default" },
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: "run `install conductor` (or `setup-epic`) for this repository to write the project-default supervisor posture.",
173
- supervisorConfig: "run `install conductor` for this repository to write the project-default supervisor configuration.",
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: "connect GitHub from the Bridge setup UI (`install-bridge connect-github`) so owner, repository id, and installation id all resolve.",
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
- reconcilerStale: "start the reconciler for this repository (the `epic-implementer epic-tick` schedule) so ticks resume.",
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: "provision and start an executor (`install conductor --executor-id <id> --repo-wide` or `executor install-service`); readiness comes only from a live server observation.",
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
- ciWorkflowMissingGuard: "add the migration-guard job to the conductor CI workflow on the default branch so migrations are checked before a merge.",
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
- repositoryWorkflowReadiness: "run `install-bridge conductor` so the review and conductor-CI workflows are confirmed on the default branch, or run setup with --attended.",
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
- : { remediation: outcome.remediation ?? SERVER_REMEDIATIONS.olderServer }),
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 = ADMISSION_OWNER_REMEDIATION[record.next_action_owner ?? "operator"] ?? ADMISSION_OWNER_REMEDIATION.operator;
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 conductor runs",
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 conductor-CI workflows are both confirmed",
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
- detail: "the review and conductor-CI workflows are not both confirmed — an unattended auto-merging run will be refused",
528
- remediation: SERVER_REMEDIATIONS.repositoryWorkflowReadiness,
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: "never observed by the server",
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 conductor-CI workflow facts",
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
- return c.migration_guard_present
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: "warn",
785
- detail: "the conductor CI workflow does not run the migration guard",
786
- remediation: SERVER_REMEDIATIONS.ciWorkflowMissingGuard,
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
- "run `install conductor` for this repository to re-provision this capability.",
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 {