@bridge_gpt/mcp-server 0.2.68 → 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.
Files changed (166) hide show
  1. package/README.md +226 -320
  2. package/build/agent-launchers/claude-executor-adapter.js +17 -4
  3. package/build/agents.generated.js +4 -4
  4. package/build/artifact-diff.js +110 -0
  5. package/build/bin-entrypoint.js +36 -0
  6. package/build/bridge-permissions.js +650 -98
  7. package/build/check-update-cli.js +74 -0
  8. package/build/claude-review-workflow-drift.js +3 -3
  9. package/build/claude-settings.js +84 -2
  10. package/build/cli-help.js +7 -1
  11. package/build/codex-bridge-profile.js +1 -1
  12. package/build/codex-home.js +9 -1
  13. package/build/codex-launcher-pin.js +598 -0
  14. package/build/commands.generated.js +8 -8
  15. package/build/conductor/bridge-api-client.js +598 -19
  16. package/build/conductor/bring-up-facts.js +9 -9
  17. package/build/conductor/doctor.js +466 -18
  18. package/build/conductor/epic-child-continuation.js +54 -3
  19. package/build/conductor/epic-implementer-cli.js +132 -34
  20. package/build/conductor/epic-runtime.js +2 -2
  21. package/build/conductor/epic-state.js +1 -1
  22. package/build/conductor/hook-runtime-qualification.js +1 -1
  23. package/build/conductor/install-doctor.js +4 -2
  24. package/build/conductor/launch-need-probes.js +337 -0
  25. package/build/conductor/local-merge.js +241 -56
  26. package/build/conductor/merge-credential.js +172 -0
  27. package/build/conductor/merge-execution-eligibility.js +1 -1
  28. package/build/conductor/merged-child-check.js +95 -0
  29. package/build/conductor/plan-trim.js +30 -0
  30. package/build/conductor/pr-ci-producer.js +1 -1
  31. package/build/conductor/readiness-cli.js +11 -6
  32. package/build/conductor/readiness-prerequisites.js +41 -0
  33. package/build/conductor/readiness-sections.js +345 -55
  34. package/build/conductor/readiness.js +99 -87
  35. package/build/conductor/recovery-cli.js +606 -21
  36. package/build/conductor/recovery-operations.js +306 -7
  37. package/build/conductor/repair-contract.js +47 -2
  38. package/build/conductor/repository-workflow-remedy.js +8 -6
  39. package/build/conductor/stored-merge-credential.js +76 -0
  40. package/build/conductor/stored-merge-token-probe.js +28 -0
  41. package/build/conductor/temporary-plan.js +50 -0
  42. package/build/conductor/wait-run-cli.js +759 -0
  43. package/build/conductor/worker-database-doctor-inspector.js +49 -0
  44. package/build/conductor-bin.js +2 -2
  45. package/build/conductor-claude-hook-bin.js +2 -2
  46. package/build/conductor-claude-hook-removed-stub-bin.js +2 -5
  47. package/build/conductor-removed-stub-bin.js +2 -5
  48. package/build/connect-github-api.js +33 -14
  49. package/build/credential-store.js +356 -13
  50. package/build/docs.generated.js +27 -14
  51. package/build/doctor.js +203 -38
  52. package/build/drive-epic-help.js +212 -0
  53. package/build/drive-epic.js +1840 -373
  54. package/build/epic-ci-workflow-drift.js +77 -0
  55. package/build/epic-ci-workflow-install.js +183 -0
  56. package/build/epic-ci-workflow.js +157 -0
  57. package/build/epic-contract-context.js +326 -0
  58. package/build/epic-implementer-bin.js +131 -120
  59. package/build/epic-implementer-claude-hook-bin.js +2 -2
  60. package/build/escalation-reachability.js +134 -0
  61. package/build/executor/cleanup-report.js +99 -0
  62. package/build/executor/cli.js +12 -1
  63. package/build/executor/completion-contract.js +68 -11
  64. package/build/executor/completion-supervision.js +99 -0
  65. package/build/executor/delivery-outcome.js +179 -0
  66. package/build/executor/deps.js +3 -0
  67. package/build/executor/env.js +71 -0
  68. package/build/executor/failure-reason-codes.js +126 -0
  69. package/build/executor/heartbeat.js +38 -0
  70. package/build/executor/http-client.js +115 -4
  71. package/build/executor/integration-job.js +298 -5
  72. package/build/executor/job-errors.js +36 -6
  73. package/build/executor/job-runner.js +720 -36
  74. package/build/executor/likely-tests.js +36 -2
  75. package/build/executor/merge-job.js +145 -2
  76. package/build/executor/observation.js +73 -4
  77. package/build/executor/permissions.js +193 -97
  78. package/build/executor/preflight.js +14 -3
  79. package/build/executor/process-containment.js +28 -2
  80. package/build/executor/prompt-spec.js +50 -5
  81. package/build/executor/rebase-finalization.js +38 -3
  82. package/build/executor/reconcile-overlap-job.js +35 -7
  83. package/build/executor/required-work-ledger.js +236 -81
  84. package/build/executor/resume-pre-spawn.js +38 -1
  85. package/build/executor/runner.js +91 -3
  86. package/build/executor/test-clock.js +8 -0
  87. package/build/executor/types.js +37 -2
  88. package/build/executor/worker-cli-launcher.js +259 -0
  89. package/build/executor/worker-command.js +41 -4
  90. package/build/executor/worker-database-target.js +91 -0
  91. package/build/executor/worker-database.js +908 -0
  92. package/build/executor/worker-finalization.js +106 -0
  93. package/build/executor/worker-guard-hook.js +203 -0
  94. package/build/executor/worker-resources.js +47 -0
  95. package/build/executor/worker-verification-guidance.js +15 -2
  96. package/build/executor/worktree.js +303 -15
  97. package/build/executor-launch.js +11 -1
  98. package/build/generated-assets-doctor.js +9 -1
  99. package/build/generated-assets-health.js +13 -4
  100. package/build/generated-assets-legacy.js +39 -0
  101. package/build/generated-assets-paths.js +12 -1
  102. package/build/generated-assets-repair.js +4 -1
  103. package/build/generated-assets-test-support.js +12 -0
  104. package/build/github-remote.js +33 -0
  105. package/build/github-sign-in.js +62 -0
  106. package/build/index.js +1214 -465
  107. package/build/init.js +39 -1
  108. package/build/install-bridge-conductor-gate.js +78 -0
  109. package/build/install-bridge-conductor-setup.js +413 -0
  110. package/build/install-bridge-conductor.js +1226 -267
  111. package/build/install-bridge.js +629 -350
  112. package/build/launcher-config-inspection.js +184 -19
  113. package/build/mcp-host-targets.js +4 -0
  114. package/build/mcp-identity.js +7 -0
  115. package/build/mcp-install-state.js +8 -2
  116. package/build/merge-pull-request.js +6 -0
  117. package/build/merge-workflow-install.js +763 -345
  118. package/build/phase-result-artifacts.js +75 -20
  119. package/build/pipeline-orchestrator.js +247 -4
  120. package/build/pipelines.generated.js +35 -35
  121. package/build/plan-discovery.js +15 -2
  122. package/build/plan-execution-ledger.js +306 -17
  123. package/build/plan-phase-routing.js +48 -13
  124. package/build/plane/cli.js +105 -51
  125. package/build/plane/defaults.js +3 -0
  126. package/build/plane/plane-freshness.js +294 -0
  127. package/build/plane/schema-compatibility.js +137 -0
  128. package/build/plane/shutdown-deps.js +38 -0
  129. package/build/plane/shutdown.js +83 -8
  130. package/build/plane/terminal-run-cleanup.js +303 -0
  131. package/build/printed-remedy-commands.js +26 -4
  132. package/build/readiness-check.js +6 -2
  133. package/build/readiness-prerequisites.generated.js +532 -0
  134. package/build/readme.generated.js +1 -1
  135. package/build/session-upgrade-notice.js +86 -0
  136. package/build/setup-epic.js +400 -30
  137. package/build/shared-resources.generated.js +5 -0
  138. package/build/start-tickets.js +43 -15
  139. package/build/test-command-detection.js +247 -0
  140. package/build/test-command-validation.js +73 -0
  141. package/build/testing-config-client.js +150 -0
  142. package/build/ticket-delivery-artifacts.js +258 -0
  143. package/build/ticket-delivery-enrollment.js +306 -0
  144. package/build/ticket-delivery.js +205 -0
  145. package/build/ticket-review-summary.js +14 -0
  146. package/build/tool-call-advisory.js +162 -0
  147. package/build/tool-surface-gating.js +28 -89
  148. package/build/update-check.js +97 -16
  149. package/build/update-status.js +61 -71
  150. package/build/upgrade-advice.js +15 -11
  151. package/build/upgrade-cli.js +360 -20
  152. package/build/v2-readiness-requirements.js +552 -0
  153. package/build/version.generated.js +3 -3
  154. package/build/worker-guard-hook-bin.js +2 -2
  155. package/build/worktree-core.js +22 -7
  156. package/docs/BRIDGE-DIRECTORY-README.md +48 -0
  157. package/docs/EPIC-IMPLEMENTER-INSTALL.md +12 -867
  158. package/docs/EPIC-IMPLEMENTER.md +104 -63
  159. package/docs/epic-implementer/getting-started.md +131 -0
  160. package/docs/epic-implementer/self-hosting.md +133 -0
  161. package/package.json +3 -3
  162. package/pipelines/idea-to-ticket.json +1 -1
  163. package/pipelines/implement-ticket.json +8 -8
  164. package/pipelines/review-ticket.json +2 -2
  165. package/design-assets/favicon/favicon-32x32.png +0 -0
  166. package/design-assets/just-logo-rough-draft.png +0 -0
@@ -0,0 +1,552 @@
1
+ import { mergeReadinessPrerequisites, formatReadinessPrerequisites } from "./conductor/readiness-prerequisites.js";
2
+ import { READINESS_PREREQUISITE_BY_ID, READINESS_LEGACY_IDS } from "./readiness-prerequisites.generated.js";
3
+ /**
4
+ * The v2 readiness requirement catalog and its row projection (BAPI-1452).
5
+ *
6
+ * Moved out of `drive-epic.ts` so the requirement-to-row projection has ONE
7
+ * shared, explicitly typed home that both the launch classifier
8
+ * (`classifyV2Readiness` in `drive-epic.ts`) and the advisory readiness report
9
+ * (`conductor/readiness.ts`) can read without either importing the other. The
10
+ * predicates and remedies are unchanged from their `drive-epic.ts` versions;
11
+ * what is new is the explicit `governedRowIds` list on every requirement.
12
+ *
13
+ * ## Blocking is applicability, never status
14
+ *
15
+ * A readiness row is `blocking` exactly when an APPLICABLE requirement lists its
16
+ * ID. A failing row no requirement governs stays non-blocking, and a passing
17
+ * governed row stays blocking — the flag says "this row can refuse this launch",
18
+ * not "this row is red". The unattended-only requirements are inapplicable for
19
+ * `--attended` and `--into-base`, so their rows are informational there.
20
+ *
21
+ * Dependency-light by design: no CLI orchestration, no filesystem, no process
22
+ * execution. It imports the readiness response type, the shared remedy
23
+ * formatter, the readiness-check constructor, and the merge credential key
24
+ * names (from the dependency-free `conductor/merge-credential.ts`), nothing else.
25
+ */
26
+ import { gateProfileWaivesCiWorkflow, isProviderRepositoryIdentity, } from "./conductor/bridge-api-client.js";
27
+ import { repositoryWorkflowRemedy } from "./conductor/repository-workflow-remedy.js";
28
+ import { LOCAL_MERGE_CREDENTIAL_ENV_KEYS } from "./conductor/merge-credential.js";
29
+ import { createReadinessCheckSafely } from "./readiness-check.js";
30
+ /**
31
+ * The v2 readiness predicate, as a list so a failure can name what is missing.
32
+ *
33
+ * These are the facts v2 actually needs to drive an epic: a stored supervisor
34
+ * posture, a reconciler that is ticking, a provisioned and live executor, and
35
+ * complete GitHub App credentials. Each is read from the SERVER's report and
36
+ * never re-derived locally — two implementations of one classification drift,
37
+ * and a CLI that disagreed with the server about whether v2 can run would be
38
+ * worse than no check at all.
39
+ */
40
+ const LEGACY_V2_READINESS_REQUIREMENTS = [
41
+ {
42
+ id: "supervisor_setup",
43
+ governedRowIds: ["server.supervisor-setup"],
44
+ describe: "supervisor setup stored for this repository",
45
+ satisfied: (r) => r.supervisor.setup_present,
46
+ },
47
+ {
48
+ id: "supervisor_config",
49
+ governedRowIds: ["server.supervisor-config"],
50
+ describe: "supervisor configuration stored for this repository",
51
+ satisfied: (r) => r.supervisor.config_present,
52
+ },
53
+ {
54
+ id: "github_credentials",
55
+ governedRowIds: ["server.github-credentials"],
56
+ describe: "GitHub App credentials that resolve completely",
57
+ satisfied: (r) => r.github.credentials_complete,
58
+ },
59
+ {
60
+ id: "reconciler_live",
61
+ governedRowIds: ["server.worker-present", "server.single-transition-authority", "server.reconciler-liveness"],
62
+ describe: "a reconciler that is ticking and not stale",
63
+ // BAPI-1279 — launch liveness is a DEPLOYMENT fact. The `bring_up` block
64
+ // reads the reconciler's process heartbeat deployment-wide, so a repository
65
+ // that has never run an epic still reads live when the worker is healthy.
66
+ // The repository-scoped `reconciler` section ("is this repository's work
67
+ // moving?", from `epic_runs.last_tick_at`) is only the fallback for a server
68
+ // too old to report `bring_up`.
69
+ satisfied: (r) => r.bring_up
70
+ ? r.bring_up.heartbeat_readable && r.bring_up.worker_present &&
71
+ r.bring_up.single_transition_authority
72
+ : r.reconciler.liveness_readable && !r.reconciler.stale,
73
+ },
74
+ {
75
+ id: "executor_live",
76
+ governedRowIds: ["server.executor-readiness"],
77
+ describe: "an executor provisioned and reporting ready",
78
+ // BAPI-1279 — diagnostic only. drive-epic starts (or, for a durable profile,
79
+ // verifies) the run's executor itself once the run exists.
80
+ advisoryOnly: true,
81
+ satisfied: (r) => r.executor.liveness_readable && r.executor.ready === true,
82
+ },
83
+ // BAPI-1102 — the three unattended prerequisites, in the order `setup-epic`
84
+ // refuses them, so readiness and the refusal name the same first blocker.
85
+ //
86
+ // Each reads the SERVER's `unattended` block and re-derives nothing. A server
87
+ // that does not report the block (older than BAPI-1102) leaves every one of
88
+ // them UNSATISFIED: "could not be read" is not "holds", and these authorize an
89
+ // unattended merge.
90
+ //
91
+ // The FIRST of them therefore carries the older-server remedy too, because it
92
+ // is the one an unreported block always blocks on, and telling that operator
93
+ // to set a consent flag would send them to fix something that may already be
94
+ // set — the server simply never said (BAPI-1102 review).
95
+ {
96
+ id: "unattended_consent",
97
+ governedRowIds: ["server.unattended-posture"],
98
+ describe: "repository consent to unattended Epic Implementer runs",
99
+ unattendedOnly: true,
100
+ // No fixed `remedy`: this requirement's action always depends on WHY.
101
+ remedyFor: (r) => r.unattended === null || r.unattended === undefined
102
+ ? "This Bridge API deploy does not report a repository's unattended " +
103
+ "prerequisites, so they could not be confirmed. Retry readiness, or use --attended."
104
+ : "Unattended runs are off for this repository. Turn on 'Allow unattended " +
105
+ "Epic Implementer runs' on the repository's Setup page in the Bridge " +
106
+ "admin panel, or pass --attended. Retry readiness after changing the setting.",
107
+ satisfied: (r) => r.unattended?.conductor_allowed === true,
108
+ },
109
+ {
110
+ id: "unattended_notify_default",
111
+ governedRowIds: ["server.unattended-notify-default"],
112
+ describe: "an escalation channel (a verified notify webhook or the account email)",
113
+ unattendedOnly: true,
114
+ remedy: "Supply a --policy-file declaring notify.webhook_url, or add an email address " +
115
+ "to the account so escalation alerts can be emailed, or pass --attended. " +
116
+ "notify.local_sink does not satisfy this.",
117
+ // BAPI-1274 — the account's email address is an escalation channel when no
118
+ // webhook is declared; the server reports its presence, never the address.
119
+ satisfied: (r) => r.unattended?.notify_webhook_default_declared === true ||
120
+ r.unattended?.escalation_email_available === true,
121
+ },
122
+ {
123
+ id: "repository_readiness",
124
+ governedRowIds: ["server.repository-workflow-readiness"],
125
+ describe: "confirmed review and epic-implementer-ci workflows on the default branch",
126
+ unattendedOnly: true,
127
+ // BAPI-1359 (Miss 38): the remedy names the SPECIFIC missing item, from the
128
+ // same shared formatter the readiness row and the setup-epic refusal use. The
129
+ // fixed install-command sentence that stood here could never clear a missing
130
+ // migration-guard job — no install step creates one — so it is gone from this
131
+ // refusal.
132
+ //
133
+ // BAPI-1390: the server decides which files are required (the review file
134
+ // only for a `verdict_protocol` run; the CI lane file only for a repository
135
+ // with no STORED gate profile and no `no_ci_gate`), and `satisfied` reads that
136
+ // decision as given. The remedy waives the CI file the same way, so a stored
137
+ // profile is never told to add a file it does not need. Gate-profile WARNINGS
138
+ // (epic-branch rows) are not requirements and are never read here.
139
+ remedyFor: (r) => repositoryWorkflowRemedy(gateProfileWaivesCiWorkflow(r) ? { ...r, conductor_ci_workflow: null } : r),
140
+ satisfied: (r) => r.unattended?.repository_readiness_confirmed === true,
141
+ },
142
+ // BAPI-1135 — merge-execution eligibility is deliberately NOT a requirement here.
143
+ //
144
+ // It was one, and it made `drive-epic` refuse EVERY repository the moment it
145
+ // shipped. The requirement could only be keyed on repository facts, because
146
+ // this layer keeps the composed run policy opaque to itself — and no
147
+ // repository carries the merge workflow until epic child 4 lands it. So both
148
+ // states resolved the same way: an upgraded server reported
149
+ // `merge_workflow_present: false` (`not_eligible`), an older one reported no
150
+ // block at all (`could_not_verify`), and either made `selectConductor` return
151
+ // `not_ready` and `classifyV2Readiness` return `operator_blocked` — for
152
+ // `--attended`, `--into-base` and `local`-mode runs alike, with a remedy
153
+ // naming a flag that does not exist yet.
154
+ //
155
+ // That is exactly the failure `selectConductor`'s own docstring warns about,
156
+ // and it contradicts the compatibility guarantee the rest of this slice keeps:
157
+ // nothing changes for a run that does not declare `github_actions`.
158
+ //
159
+ // Eligibility is still enforced, at the three layers that can actually see the
160
+ // declared mode: `setup-epic`'s pre-create refusal (which reads the composed
161
+ // policy), the approval route's server-side gate, and — authoritatively — the
162
+ // before-merge preflight. Readiness still REPORTS the facts as diagnostic
163
+ // rows, which inform without blocking. Reinstating a requirement here belongs
164
+ // with child 4, once a repository can actually satisfy it.
165
+ ];
166
+ /** The three additive outcomes are authoritative; absent or unknown never denies. */
167
+ const NEW_IDS = ['server.indexed-workflows', 'server.unattended-notify-default', 'server.test-command'];
168
+ export const V2_READINESS_REQUIREMENTS = [
169
+ ...LEGACY_V2_READINESS_REQUIREMENTS.map(req => {
170
+ const id = READINESS_LEGACY_IDS[req.id] ?? req.id;
171
+ const descriptor = READINESS_PREREQUISITE_BY_ID.get(id);
172
+ return { ...req, id, describe: descriptor?.label ?? req.describe,
173
+ governedRowIds: req.governedRowIds.map(row => READINESS_LEGACY_IDS[row] ?? row),
174
+ ...(req.id === 'repository_readiness' ? { satisfied: (r) => r.prerequisites !== undefined ? true : req.satisfied(r) } : {}),
175
+ ...(req.id === 'unattended_notify_default' ? {
176
+ satisfied: (r) => !r.prerequisites?.some(p => p.id === id && p.check_status === 'not_met' && p.impact === 'blocking'),
177
+ remedyFor: (r) => r.prerequisites?.find(p => p.id === id)?.fix ?? 'Retry readiness.',
178
+ } : {}),
179
+ };
180
+ }),
181
+ ...NEW_IDS.filter(id => id !== 'server.unattended-notify-default').map(id => ({
182
+ id, describe: READINESS_PREREQUISITE_BY_ID.get(id).label, governedRowIds: [id], unattendedOnly: true,
183
+ satisfied: (r) => !r.prerequisites?.some(p => p.id === id && p.check_status === 'not_met' && p.impact === 'blocking'),
184
+ remedyFor: (r) => r.prerequisites?.find(p => p.id === id)?.fix ?? 'Retry readiness.',
185
+ })),
186
+ ];
187
+ // ---------------------------------------------------------------------------
188
+ // BAPI-1452: launch capability evidence
189
+ // ---------------------------------------------------------------------------
190
+ /** Stable row IDs for the launch summary, in render order. */
191
+ export const UNATTENDED_POSTURE_ROW_ID = "server.unattended-posture";
192
+ export const MERGE_CREDENTIAL_CAPABILITY_ROW_ID = "conductor.merge-credential-capability";
193
+ export const WORKTREE_PREPARATION_ROW_ID = "conductor.worktree-preparation";
194
+ export const RESOLVED_MERGE_MODE_ROW_ID = "server.resolved-merge-mode";
195
+ export const PROVIDER_REPOSITORY_ROW_ID = "server.provider-repository";
196
+ /** The exact Setup-page control name, kept visible in every posture refusal. */
197
+ export const UNATTENDED_POSTURE_SETUP_CONTROL = "Allow unattended Epic Implementer runs";
198
+ /**
199
+ * BAPI-1452 — the two launch capability requirements.
200
+ *
201
+ * The credential requirement does not apply to a `github_actions` run: no local
202
+ * credential merges there, so its row is informational. Both are unattended-only,
203
+ * because they protect an unattended merge and an unattended worker's commits.
204
+ */
205
+ const LEGACY_V2_LAUNCH_EVIDENCE_REQUIREMENTS = [
206
+ {
207
+ id: "merge_credential_capability",
208
+ describe: "a merge credential that has push and can read pull requests",
209
+ unattendedOnly: true,
210
+ appliesTo: (evidence) => evidence.credential.kind !== "github_actions",
211
+ governedRowIds: [MERGE_CREDENTIAL_CAPABILITY_ROW_ID],
212
+ },
213
+ {
214
+ id: "worktree_preparation",
215
+ describe: "an executor clone whose shared git exclude can be prepared",
216
+ unattendedOnly: true,
217
+ appliesTo: () => true,
218
+ governedRowIds: [WORKTREE_PREPARATION_ROW_ID],
219
+ },
220
+ ];
221
+ export const V2_LAUNCH_EVIDENCE_REQUIREMENTS = LEGACY_V2_LAUNCH_EVIDENCE_REQUIREMENTS.map(req => {
222
+ const id = READINESS_LEGACY_IDS[req.id] ?? req.id;
223
+ return { ...req, id, describe: READINESS_PREREQUISITE_BY_ID.get(id).label };
224
+ });
225
+ /** True when the context opts out of unattended operation. */
226
+ function optedOut(context) {
227
+ return context.attended === true || context.intoBase === true;
228
+ }
229
+ /**
230
+ * The row IDs that are blocking in this context: the union of every APPLICABLE
231
+ * requirement's governed rows. Advisory requirements never contribute.
232
+ */
233
+ export function applicableGovernedRowIds(context) {
234
+ const ids = new Set();
235
+ const out = optedOut(context);
236
+ for (const req of V2_READINESS_REQUIREMENTS) {
237
+ if (req.advisoryOnly === true)
238
+ continue;
239
+ if (out && req.unattendedOnly === true)
240
+ continue;
241
+ for (const id of req.governedRowIds)
242
+ ids.add(id);
243
+ }
244
+ if (context.launchEvidence !== undefined && !out) {
245
+ for (const req of V2_LAUNCH_EVIDENCE_REQUIREMENTS) {
246
+ if (!req.appliesTo(context.launchEvidence))
247
+ continue;
248
+ for (const id of req.governedRowIds)
249
+ ids.add(id);
250
+ }
251
+ }
252
+ return ids;
253
+ }
254
+ /** Every row ID any requirement governs, applicable or not. */
255
+ const ALL_GOVERNED_ROW_IDS = new Set([
256
+ ...V2_READINESS_REQUIREMENTS.flatMap((req) => req.governedRowIds),
257
+ ...V2_LAUNCH_EVIDENCE_REQUIREMENTS.flatMap((req) => req.governedRowIds),
258
+ ]);
259
+ /** True when some requirement governs this row ID (BAPI-1452). */
260
+ export function isGovernedReadinessRowId(id) {
261
+ return ALL_GOVERNED_ROW_IDS.has(id);
262
+ }
263
+ /**
264
+ * Project requirement applicability onto rows: `blocking` is membership in an
265
+ * applicable requirement's governed row IDs, and nothing else. Status is never
266
+ * read.
267
+ */
268
+ export function projectReadinessRowBlocking(checks, context = {}) {
269
+ const governed = applicableGovernedRowIds(context);
270
+ return checks.map((check) => ({ ...check, blocking: governed.has(check.id) }));
271
+ }
272
+ /** The credential configuration key a mode reads. Never the deprecated alias. */
273
+ export function launchCredentialKey(mode) {
274
+ return LOCAL_MERGE_CREDENTIAL_ENV_KEYS[mode];
275
+ }
276
+ const UNVERIFIED_CREDENTIAL_COPY = {
277
+ probe_unavailable: {
278
+ detail: "GitHub gave no usable answer to the capability check (rate limit, outage, timeout, or an inconclusive status)",
279
+ remedy: "Retry drive-epic shortly. The merge itself still checks the credential before it runs.",
280
+ },
281
+ gh_unavailable: {
282
+ detail: "the GitHub CLI (gh) could not be run on this host, so the credential could not be checked",
283
+ remedy: "Install the GitHub CLI (gh) on the host that runs the executor; local merges run through it.",
284
+ },
285
+ repository_unresolved: {
286
+ detail: "the server could not resolve this repository's GitHub owner/name, so there was nothing to check against",
287
+ remedy: "Fix the repository's GitHub App credentials (owner and repository) in Bridge, then retry drive-epic.",
288
+ },
289
+ mode_unresolved: {
290
+ detail: "the server could not resolve which merge execution mode a new run would use",
291
+ remedy: "Check the supervisor configuration's merge execution setting, then retry drive-epic.",
292
+ },
293
+ server_fields_missing: {
294
+ detail: "this Bridge API deploy does not report the resolved merge mode or the provider repository",
295
+ remedy: "Try again later. If drive-epic keeps refusing, email info@bridgegpt.ai so merge capability can be verified before launch.",
296
+ },
297
+ external_executor_plane: {
298
+ detail: "a plane lane runs this run's executor, so this launcher cannot check that executor's credential",
299
+ remedy: "Check the merge credential where the plane runs (`epic-implementer doctor` on that host).",
300
+ },
301
+ external_executor_durable: {
302
+ detail: "the durable launch profile's host runs this run's executor, so this launcher cannot check its credential",
303
+ remedy: "Check the merge credential on the durable executor host (`epic-implementer doctor` there).",
304
+ },
305
+ };
306
+ /** The merge credential row. Value-free: names keys and `owner/name`, never a token. */
307
+ function credentialCheck(evidence) {
308
+ const base = {
309
+ id: MERGE_CREDENTIAL_CAPABILITY_ROW_ID,
310
+ source: "conductor",
311
+ label: "Merge credential capability",
312
+ };
313
+ if (evidence === null) {
314
+ return createReadinessCheckSafely({
315
+ ...base,
316
+ status: "skip",
317
+ detail: "not checked — this launch is attended, so no unattended merge runs",
318
+ });
319
+ }
320
+ switch (evidence.kind) {
321
+ case "verified": {
322
+ const key = launchCredentialKey(evidence.mode);
323
+ return createReadinessCheckSafely({
324
+ ...base,
325
+ status: "pass",
326
+ detail: `${key} has push and can read pull requests on ${evidence.repository}. ` +
327
+ "Branch protection and rulesets are still checked at merge time.",
328
+ });
329
+ }
330
+ case "github_actions":
331
+ return createReadinessCheckSafely({
332
+ ...base,
333
+ status: "skip",
334
+ detail: "merges run through the GitHub Actions merge workflow, so no local merge credential is checked",
335
+ });
336
+ case "credential_missing": {
337
+ const key = launchCredentialKey(evidence.mode);
338
+ return createReadinessCheckSafely({
339
+ ...base,
340
+ status: "fail",
341
+ detail: `${key} is not set in the environment drive-epic starts the executor with`,
342
+ remediation: `Set ${key} in the environment you run drive-epic from, then run drive-epic again. ` +
343
+ "Bridge never stores it.",
344
+ });
345
+ }
346
+ case "credential_invalid": {
347
+ const key = launchCredentialKey(evidence.mode);
348
+ return createReadinessCheckSafely({
349
+ ...base,
350
+ status: "fail",
351
+ detail: `${key} is set but is not a usable credential (malformed, or rejected by GitHub)`,
352
+ remediation: `Replace ${key} with a valid token, then run drive-epic again.`,
353
+ });
354
+ }
355
+ case "no_access": {
356
+ const key = launchCredentialKey(evidence.mode);
357
+ return createReadinessCheckSafely({
358
+ ...base,
359
+ status: "fail",
360
+ detail: `${key} cannot push to ${evidence.repository} or cannot read its pull requests`,
361
+ remediation: `Give the account behind ${key} push (write) access to ${evidence.repository}, ` +
362
+ "or use a token that has it, then run drive-epic again.",
363
+ });
364
+ }
365
+ case "unverified": {
366
+ const copy = UNVERIFIED_CREDENTIAL_COPY[evidence.reason];
367
+ return createReadinessCheckSafely({
368
+ ...base,
369
+ status: "warn",
370
+ detail: `Capability not verified: ${copy.detail}`,
371
+ remediation: copy.remedy,
372
+ });
373
+ }
374
+ }
375
+ }
376
+ /** The worktree-preparation row. Never carries the absolute exclude path. */
377
+ function worktreeCheck(evidence) {
378
+ const base = {
379
+ id: WORKTREE_PREPARATION_ROW_ID,
380
+ source: "conductor",
381
+ label: "Worktree preparation",
382
+ };
383
+ if (evidence === null) {
384
+ return createReadinessCheckSafely({
385
+ ...base,
386
+ status: "skip",
387
+ detail: "not checked — this launch is attended",
388
+ });
389
+ }
390
+ switch (evidence.kind) {
391
+ case "ready":
392
+ return createReadinessCheckSafely({
393
+ ...base,
394
+ status: "pass",
395
+ detail: evidence.present
396
+ ? "the executor clone's shared git exclude already lists the executor's entries"
397
+ : "the executor clone's shared git exclude can be written when the executor prepares it",
398
+ });
399
+ case "blocked":
400
+ return createReadinessCheckSafely({
401
+ ...base,
402
+ status: "fail",
403
+ detail: "the executor clone's shared git exclude is missing the executor's entries and cannot be written",
404
+ remediation: "Make the clone's .git/info/exclude (or its info directory) writable by the user that runs the " +
405
+ "executor, then run drive-epic again.",
406
+ });
407
+ case "unverified":
408
+ return createReadinessCheckSafely({
409
+ ...base,
410
+ status: "warn",
411
+ detail: evidence.reason === "inspection_unavailable"
412
+ ? "Capability not verified: the executor clone's shared git exclude could not be inspected"
413
+ : evidence.reason === "external_executor_plane"
414
+ ? "Capability not verified: a plane lane owns this run's executor clone, so this launcher cannot inspect it"
415
+ : "Capability not verified: the durable launch profile's host owns this run's executor clone, so this launcher cannot inspect it",
416
+ remediation: evidence.reason === "inspection_unavailable"
417
+ ? "The executor still prepares the exclude itself at startup; run `epic-implementer doctor` if workers later commit executor files."
418
+ : "That executor still prepares its own clone at startup; inspect it on the host that runs it.",
419
+ });
420
+ }
421
+ }
422
+ /** The Setup-page posture row, from the server's unattended fact only. */
423
+ export function unattendedPostureCheck(readiness) {
424
+ const requirement = V2_READINESS_REQUIREMENTS.find((req) => req.id === "server.unattended-posture");
425
+ const remedy = requirement?.remedyFor?.(readiness) ??
426
+ `Turn on '${UNATTENDED_POSTURE_SETUP_CONTROL}' on the repository's Setup page in the Bridge admin panel.`;
427
+ const base = {
428
+ id: UNATTENDED_POSTURE_ROW_ID,
429
+ source: "server",
430
+ label: "Unattended posture (Setup page)",
431
+ };
432
+ if (readiness.unattended === null || readiness.unattended === undefined) {
433
+ return createReadinessCheckSafely({
434
+ ...base,
435
+ status: "fail",
436
+ detail: "this Bridge API deploy does not report the Setup-page unattended switch",
437
+ remediation: remedy,
438
+ });
439
+ }
440
+ return readiness.unattended.conductor_allowed === true
441
+ ? createReadinessCheckSafely({
442
+ ...base,
443
+ status: "pass",
444
+ detail: `'${UNATTENDED_POSTURE_SETUP_CONTROL}' is on for this repository`,
445
+ })
446
+ : createReadinessCheckSafely({
447
+ ...base,
448
+ status: "fail",
449
+ detail: `'${UNATTENDED_POSTURE_SETUP_CONTROL}' is off for this repository, so an unattended run is refused`,
450
+ remediation: remedy,
451
+ });
452
+ }
453
+ /** The resolved merge mode, as a supporting fact. Absent or null is a warning, never a default. */
454
+ export function resolvedMergeModeCheck(readiness) {
455
+ const base = { id: RESOLVED_MERGE_MODE_ROW_ID, source: "server", label: "Resolved merge mode" };
456
+ const mode = readiness.resolved_merge_execution_mode;
457
+ if (mode === undefined) {
458
+ return createReadinessCheckSafely({
459
+ ...base,
460
+ status: "warn",
461
+ detail: "Capability not verified: this Bridge API deploy does not report the resolved merge mode",
462
+ remediation: UNVERIFIED_CREDENTIAL_COPY.server_fields_missing.remedy,
463
+ });
464
+ }
465
+ if (mode === null) {
466
+ return createReadinessCheckSafely({
467
+ ...base,
468
+ status: "warn",
469
+ detail: "Capability not verified: the server could not resolve a supported merge mode",
470
+ remediation: UNVERIFIED_CREDENTIAL_COPY.mode_unresolved.remedy,
471
+ });
472
+ }
473
+ return createReadinessCheckSafely({ ...base, status: "pass", detail: `a new run merges in ${mode} mode` });
474
+ }
475
+ /** The provider repository, as a supporting fact. */
476
+ export function providerRepositoryCheck(readiness) {
477
+ const base = { id: PROVIDER_REPOSITORY_ROW_ID, source: "server", label: "Provider repository" };
478
+ const repository = readiness.provider_repository;
479
+ if (repository === undefined) {
480
+ return createReadinessCheckSafely({
481
+ ...base,
482
+ status: "warn",
483
+ detail: "Capability not verified: this Bridge API deploy does not report the provider repository",
484
+ remediation: UNVERIFIED_CREDENTIAL_COPY.server_fields_missing.remedy,
485
+ });
486
+ }
487
+ if (!isProviderRepositoryIdentity(repository)) {
488
+ return createReadinessCheckSafely({
489
+ ...base,
490
+ status: "warn",
491
+ detail: "Capability not verified: the server could not resolve this repository's GitHub owner/name",
492
+ remediation: UNVERIFIED_CREDENTIAL_COPY.repository_unresolved.remedy,
493
+ });
494
+ }
495
+ return createReadinessCheckSafely({ ...base, status: "pass", detail: `GitHub repository ${repository}` });
496
+ }
497
+ /**
498
+ * The launch readiness rows, in render order: posture, merge credential
499
+ * capability, worktree preparation, then the supporting facts. `blocking` is
500
+ * projected from requirement applicability only.
501
+ */
502
+ export function buildLaunchReadinessChecks(readiness, context) {
503
+ const evidence = optedOut(context) ? undefined : context.launchEvidence;
504
+ return projectReadinessRowBlocking([
505
+ unattendedPostureCheck(readiness),
506
+ credentialCheck(evidence?.credential ?? null),
507
+ worktreeCheck(evidence?.worktree ?? null),
508
+ resolvedMergeModeCheck(readiness),
509
+ providerRepositoryCheck(readiness),
510
+ ], context);
511
+ }
512
+ /** The two launch-evidence rows a launch can be refused on. */
513
+ export const LAUNCH_EVIDENCE_ROW_IDS = [
514
+ MERGE_CREDENTIAL_CAPABILITY_ROW_ID,
515
+ WORKTREE_PREPARATION_ROW_ID,
516
+ ];
517
+ /**
518
+ * The first PROVEN launch-evidence blocker: a failing row an applicable
519
+ * requirement governs. Unverified evidence is a warning and never returned.
520
+ */
521
+ export function firstLaunchEvidenceBlocker(checks) {
522
+ for (const id of LAUNCH_EVIDENCE_ROW_IDS) {
523
+ const check = checks.find((c) => c.id === id);
524
+ if (check && check.status === "fail" && check.blocking)
525
+ return check;
526
+ }
527
+ return null;
528
+ }
529
+ /**
530
+ * The applicability label a readiness row renders with (BAPI-1452).
531
+ *
532
+ * `Blocking` only for a FAILED row an applicable requirement governs; `Warning`
533
+ * for any warning (unknown evidence is never green); `Informational` for a row
534
+ * no applicable requirement governs; `OK` for a passing governed row.
535
+ */
536
+ export function readinessApplicabilityLabel(check) {
537
+ if (check.detail === 'unknown')
538
+ return "Couldn't check";
539
+ if (check.detail === 'not_met' && !check.blocking)
540
+ return 'Not met';
541
+ if (check.status === "fail" && check.blocking)
542
+ return "Blocking";
543
+ if (check.status === "warn")
544
+ return "Warning";
545
+ if (!check.blocking || check.status === "fail" || check.status === "skip")
546
+ return "Informational";
547
+ return "OK";
548
+ }
549
+ /** Render the compact launch summary drive-epic prints before creating a run. */
550
+ export function formatLaunchReadinessSummary(checks, options = {}) {
551
+ return formatReadinessPrerequisites(mergeReadinessPrerequisites(checks, options.prerequisites), options);
552
+ }
@@ -1,4 +1,4 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
- export const VERSION = "0.2.68";
3
- export const BUILD_COMMIT = "2aef1c5656bc";
4
- export const LAUNCHER_ARGS = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.68", "serve"];
2
+ export const VERSION = "0.2.70";
3
+ export const BUILD_COMMIT = "7fb22e3a1d7b";
4
+ export const LAUNCHER_ARGS = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.70", "serve"];