@bridge_gpt/mcp-server 0.2.67 → 0.2.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +178 -15
  2. package/build/agent-launchers/claude-executor-adapter.js +27 -0
  3. package/build/agents.generated.js +22 -2
  4. package/build/bitbucket-workspace-remote.js +136 -0
  5. package/build/bootstrap-project-identity.js +24 -0
  6. package/build/bridge-permissions.js +829 -0
  7. package/build/claude-settings.js +18 -0
  8. package/build/cli-help.js +21 -0
  9. package/build/command-assets-doctor.js +24 -70
  10. package/build/command-provisioning.js +29 -5
  11. package/build/commands.generated.js +8 -8
  12. package/build/conductor/bridge-api-client.js +714 -0
  13. package/build/conductor/command-drift-production.js +92 -0
  14. package/build/conductor/command-drift.js +89 -0
  15. package/build/conductor/doctor.js +69 -26
  16. package/build/conductor/epic-branch-ci-inspection.js +1375 -0
  17. package/build/conductor/epic-implementer-cli.js +76 -22
  18. package/build/conductor/gate-discovery.js +890 -0
  19. package/build/conductor/install-doctor.js +15 -3
  20. package/build/conductor/json-output.js +63 -0
  21. package/build/conductor/readiness-cli.js +95 -10
  22. package/build/conductor/readiness-sections.js +369 -3
  23. package/build/conductor/readiness.js +211 -8
  24. package/build/conductor/recovery-cli.js +110 -4
  25. package/build/conductor/recovery-operations.js +99 -1
  26. package/build/connect-bitbucket-api.js +41 -6
  27. package/build/connect-bitbucket.js +106 -33
  28. package/build/credential-store.js +207 -47
  29. package/build/docs.generated.js +57 -3
  30. package/build/doctor.js +92 -1
  31. package/build/drive-epic.js +45 -8
  32. package/build/epic-coherence-input.test-fixtures.js +4 -3
  33. package/build/epic-implementer-bin.js +1374 -25
  34. package/build/epic-workarounds-path.js +39 -0
  35. package/build/executor/cli.js +30 -0
  36. package/build/executor/completion-contract.js +18 -5
  37. package/build/executor/deps.js +4 -1
  38. package/build/executor/gate-qualification-job.js +499 -0
  39. package/build/executor/guarded-fast-forward-push.js +129 -0
  40. package/build/executor/integration-job.js +122 -8
  41. package/build/executor/job-errors.js +8 -1
  42. package/build/executor/job-runner.js +589 -47
  43. package/build/executor/likely-tests.js +290 -0
  44. package/build/executor/live-worker-registry.js +1 -1
  45. package/build/executor/observation.js +31 -2
  46. package/build/executor/packaged-recipe.js +106 -0
  47. package/build/executor/permissions.js +115 -28
  48. package/build/executor/platform.js +1 -1
  49. package/build/executor/preflight.js +61 -12
  50. package/build/executor/process-containment.js +36 -12
  51. package/build/executor/process.js +15 -7
  52. package/build/executor/prompt-spec.js +33 -1
  53. package/build/executor/recently-failed-tests-file.js +71 -0
  54. package/build/executor/reconcile-overlap-job.js +41 -23
  55. package/build/executor/required-verification.js +1 -1
  56. package/build/executor/required-work-ledger.js +105 -17
  57. package/build/executor/retirement.js +69 -0
  58. package/build/executor/runner.js +114 -11
  59. package/build/executor/test-clock.js +15 -0
  60. package/build/executor/types.js +32 -0
  61. package/build/executor/viewer-tabs.js +58 -8
  62. package/build/executor/worker-command.js +23 -7
  63. package/build/executor/worker-diagnostics.js +37 -0
  64. package/build/executor/worker-finalization.js +31 -5
  65. package/build/executor/worker-guard-hook.js +179 -25
  66. package/build/executor/worker-log.js +4 -3
  67. package/build/executor/worker-verification-guidance.js +37 -0
  68. package/build/executor/worktree.js +88 -11
  69. package/build/first-build-advisory.js +97 -0
  70. package/build/generated-assets-advisory.js +14 -0
  71. package/build/generated-assets-catalog.js +66 -0
  72. package/build/generated-assets-context.js +46 -0
  73. package/build/generated-assets-dependencies.js +26 -0
  74. package/build/generated-assets-doctor.js +50 -0
  75. package/build/generated-assets-health.js +65 -0
  76. package/build/generated-assets-host.js +17 -0
  77. package/build/generated-assets-inventory.js +17 -0
  78. package/build/generated-assets-paths.js +60 -0
  79. package/build/generated-assets-publisher.js +133 -0
  80. package/build/generated-assets-remedy.js +25 -0
  81. package/build/generated-assets-repair.js +72 -0
  82. package/build/generated-assets-session.js +71 -0
  83. package/build/generated-assets-test-support.js +35 -0
  84. package/build/index.js +964 -768
  85. package/build/init.js +54 -177
  86. package/build/install-bridge-conductor-gate.js +1372 -0
  87. package/build/install-bridge-conductor.js +289 -36
  88. package/build/install-bridge.js +544 -37
  89. package/build/mcp-install-state.js +50 -4
  90. package/build/merge-workflow-install.js +3 -4
  91. package/build/pipeline-utils.js +6 -2
  92. package/build/pipelines.generated.js +22 -142
  93. package/build/plan-execution-ledger.js +2 -32
  94. package/build/plane/manifest.js +16 -1
  95. package/build/plane/preflight.js +54 -1
  96. package/build/printed-remedy-commands.js +16 -1
  97. package/build/readme.generated.js +1 -1
  98. package/build/repair-assets.js +93 -0
  99. package/build/run-unit-tests-launcher.js +130 -49
  100. package/build/secret-redaction.js +33 -0
  101. package/build/setup-epic.js +108 -3
  102. package/build/start-tickets-prereqs.js +70 -16
  103. package/build/start-tickets.js +29 -5
  104. package/build/tool-surface-gating.js +36 -0
  105. package/build/upgrade-cli.js +55 -1
  106. package/build/version.generated.js +3 -3
  107. package/build/worker-guard-hook-bin.js +3 -2
  108. package/build/worker-guard-hook-main.js +1 -1
  109. package/build/worktree-core.js +15 -2
  110. package/docs/EPIC-IMPLEMENTER-INSTALL.md +30 -12
  111. package/docs/EPIC-IMPLEMENTER.md +68 -2
  112. package/package.json +3 -3
  113. package/pipelines/greenfield-setup.json +4 -128
  114. package/pipelines/implement-ticket.json +3 -2
@@ -36,7 +36,7 @@ import { buildConductorDoctorReport, formatConductorDoctorReport, } from "./doct
36
36
  import { inspectBridgeApiProfileToken, } from "../mcp-host-config.js";
37
37
  import { collectExecutorServiceDiagnostics, formatExecutorServiceDiagnosticsReport, } from "../doctor.js";
38
38
  import { collectInstallStatusChecks, formatInstallStatusReport, } from "../install-doctor.js";
39
- import { bringUpSafetySections, conductorHookTargetsSection, conductorLedgerLoadabilitySection, gitHubActionsMergeObservabilitySections, conductorProfileTokenSection, EPIC_IMPLEMENTER_PROFILE_TOKEN, detectConductorContext, executorObservationSection, executorProvisioningSection, executorServiceStateSection, githubActionsSection, mergeExecutionSections, githubCredentialsSection, inspectWorkflowPresence, reconcilerSection, supervisorSection, workflowSection, } from "./readiness-sections.js";
39
+ import { bringUpSafetySections, conductorHookTargetsSection, conductorLedgerLoadabilitySection, gitHubActionsMergeObservabilitySections, conductorProfileTokenSection, effectiveReviewModeIsRepositoryPolicy, epicBranchCiSections, EPIC_IMPLEMENTER_PROFILE_TOKEN, detectConductorContext, executorObservationSection, executorProvisioningSection, executorServiceStateSection, githubActionsSection, mergeExecutionSections, githubCredentialsSection, inspectWorkflowPresence, reconcilerSection, supervisorSection, workflowSection, } from "./readiness-sections.js";
40
40
  import { ConductorBridgeApiError, fetchConductorReadiness, } from "./bridge-api-client.js";
41
41
  /**
42
42
  * Run one optional collector, containing any throw.
@@ -155,6 +155,11 @@ export async function runConductorInstallDoctor(deps) {
155
155
  }
156
156
  if (readiness) {
157
157
  sections.push(...supervisorSection(readiness));
158
+ // BAPI-1405 — the three warn-only epic-branch CI rows, beside the gate
159
+ // profile they qualify. Never fatal, so they cannot change the exit code or
160
+ // the installer's pre-write abort. An older server renders them as
161
+ // "Unreported — upgrade the server", never as "no warning".
162
+ sections.push(...epicBranchCiSections(readiness.epic_branch_ci));
158
163
  sections.push(githubCredentialsSection(readiness));
159
164
  sections.push(githubActionsSection(readiness));
160
165
  // BAPI-1135 — the shared "Merge execution eligibility" section, immediately
@@ -166,7 +171,12 @@ export async function runConductorInstallDoctor(deps) {
166
171
  sections.push(...mergeExecutionSections(readiness));
167
172
  }
168
173
  const workflow = await inspectWorkflowPresence(deps.readWorkflowFile, deps.classifyWorkflowDrift);
169
- sections.push(workflowSection(workflow.presence, deps.reviewPolicySource, workflow.driftDetail));
174
+ // BAPI-1405 — when the server says the effective review mode is
175
+ // `repository_policy`, no review verdict is consumed, so the workflow row
176
+ // reports not-applicable instead of nagging. An older server keeps the
177
+ // caller's source exactly as before.
178
+ const effectiveReviewPolicySource = effectiveReviewModeIsRepositoryPolicy(readiness) ? "repository_policy" : deps.reviewPolicySource;
179
+ sections.push(workflowSection(workflow.presence, effectiveReviewPolicySource, workflow.driftDetail));
170
180
  if (readiness) {
171
181
  // BAPI-1153 — the "Bring-up safety" group, ahead of the repository-scoped
172
182
  // reconciler/executor story: these three decide whether a plane can drive a
@@ -357,6 +367,8 @@ mergeExecutionSections, databaseMigrationCurrentSection, singleTransitionAuthori
357
367
  // BAPI-1135 — both new section builders join the compatibility surface like
358
368
  // every other one, so an existing importer of this module does not learn a
359
369
  // second import path for them.
360
- gitHubActionsMergeObservabilitySections, } from "./readiness-sections.js";
370
+ gitHubActionsMergeObservabilitySections,
371
+ // BAPI-1405 — the epic-branch CI rows and their shared copy.
372
+ epicBranchCiSections, EPIC_BRANCH_CI_RERUN_INSTALL_ADVICE, EPIC_BRANCH_CI_SECTION_IDS, EPIC_BRANCH_CI_SECTION_LABELS, EPIC_BRANCH_CI_UNREPORTED_DETAIL, } from "./readiness-sections.js";
361
373
  // BAPI-1153 — the bring-up remediations the three sections above render.
362
374
  export { BRING_UP_RUNBOOK, BRING_UP_UNREPORTED_REMEDIATION, DATABASE_MIGRATION_BEHIND_REMEDIATION, DATABASE_MIGRATION_UNREADABLE_REMEDIATION, SECOND_RECONCILER_REMEDIATION, WORKER_HEARTBEAT_UNREADABLE_REMEDIATION, WORKER_NOT_PRESENT_REMEDIATION, } from "./bring-up-facts.js";
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Complete `--json` output for CLI commands (BAPI-1397, F3).
3
+ *
4
+ * The CLI ends through `process.exit(cliExitCode)` in `index.ts` as soon as a
5
+ * command returns its exit code. On macOS a piped stdout is asynchronous, so a
6
+ * large document written with `console.log` could still be queued when the
7
+ * process exited — a 72 KB `epic-status --json` reached its reader truncated.
8
+ *
9
+ * {@link writeJsonDocument} prints the already-serialized document through the
10
+ * same sink as before (so its bytes and the tests that capture `console.log` are
11
+ * unchanged) and then waits until stdout has handed every queued byte to the
12
+ * operating system before the command returns. It never changes a payload.
13
+ */
14
+ /**
15
+ * Wait until `stream` has flushed everything written to it so far.
16
+ *
17
+ * A writable stream completes writes in order, so the callback of a zero-length
18
+ * write runs only after every earlier write has completed. Resolves `true` once
19
+ * flushed and `false` when the stream reports an error (for example a reader
20
+ * that closed the pipe). Never throws and never leaves an `error` listener
21
+ * behind, so a closed pipe cannot become an uncaught exception.
22
+ */
23
+ export function flushOutputStream(stream) {
24
+ return new Promise((resolve) => {
25
+ let settled = false;
26
+ const finish = (ok) => {
27
+ if (settled)
28
+ return;
29
+ settled = true;
30
+ stream.removeListener("error", onError);
31
+ resolve(ok);
32
+ };
33
+ const onError = () => finish(false);
34
+ stream.once("error", onError);
35
+ try {
36
+ stream.write("", (error) => finish(!error));
37
+ }
38
+ catch {
39
+ finish(false);
40
+ }
41
+ });
42
+ }
43
+ /** Flush the real process stdout. */
44
+ export function flushProcessStdout() {
45
+ return flushOutputStream(process.stdout);
46
+ }
47
+ /**
48
+ * Print one already-serialized JSON document and wait for stdout to drain.
49
+ *
50
+ * Resolves `true` when the whole document was handed to the operating system,
51
+ * `false` when the output could not complete. The text is printed exactly as
52
+ * given, so no command's JSON shape changes.
53
+ */
54
+ export async function writeJsonDocument(serialized, deps = {}) {
55
+ const log = deps.log ?? ((line) => console.log(line));
56
+ try {
57
+ log(serialized);
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ return (deps.flush ?? flushProcessStdout)();
63
+ }
@@ -35,7 +35,9 @@
35
35
  import os from "node:os";
36
36
  import { readFile as fsReadFile, stat as fsStat } from "node:fs/promises";
37
37
  import { collectConductorReadinessGate, formatConductorReadinessGateReport, } from "./readiness.js";
38
- import { readinessEndpointEnvironments, resolveConductorBridgeApiAccess, resolveEpicLaunchBridgeApiAccess } from "./bridge-api-client.js";
38
+ import { fetchEffectiveSupervisorSetup, readinessEndpointEnvironments, resolveConductorBridgeApiAccess, resolveEpicLaunchBridgeApiAccess, } from "./bridge-api-client.js";
39
+ import { createCommandDriftInspector } from "./command-drift-production.js";
40
+ import { flushProcessStdout, writeJsonDocument } from "./json-output.js";
39
41
  import { validateHttpBaseUrl } from "../base-url.js";
40
42
  import { claudeReviewWorkflowPath } from "../claude-review-workflow.js";
41
43
  import { runDefaultPlanePreflight } from "../plane/cli.js";
@@ -46,13 +48,18 @@ export const READINESS_COLLECTION_NOTICE = "one or more readiness sources could
46
48
  /** Usage text for `epic-implementer readiness`. */
47
49
  export function getReadinessUsage() {
48
50
  return [
49
- "Usage: epic-implementer readiness [--json] [--no-deny-probe] [--base-url <url>]",
51
+ "Usage: epic-implementer readiness [--json] [--no-deny-probe] [--base-url <url>] [--epic-run-id <id>]",
50
52
  "",
51
53
  "Read-only ADVISORY report of every conductor prerequisite, consolidated from the",
52
54
  "Bridge install checklist, the epic-implementer doctor, the plane preflight, and the",
53
55
  "server conductor-readiness collector. Each prerequisite reports pass/warn/fail/skip",
54
56
  "and, on a failure, exactly ONE named remediation.",
55
57
  "",
58
+ "The claude-review workflow row is assessed for a NEW feature-branch run's first",
59
+ "approval: it is not applicable only when the server confirms no repository review",
60
+ "default is set. If that cannot be confirmed the row stays required. This report is",
61
+ "not a prediction or an authorization of any particular run.",
62
+ "",
56
63
  "Remediations are NOT run automatically. This command changes nothing: it starts no",
57
64
  "plane, approves no run, writes no file, and always exits 0 — `drive-epic` remains",
58
65
  "the only route-selection gate and the server-side admission the only hard refusal.",
@@ -63,6 +70,9 @@ export function getReadinessUsage() {
63
70
  " this command never spawns the deny-enforcement probe",
64
71
  " --base-url <url> Select a read-only Bridge API endpoint for this report;",
65
72
  " this does not authorize or supply a credential",
73
+ " --epic-run-id <id>",
74
+ " The epic run being attached. When the live plane is bound to",
75
+ " this run, its port and manifest rows pass with reason own_plane",
66
76
  " --help Print this usage message",
67
77
  ].join("\n");
68
78
  }
@@ -86,11 +96,64 @@ export function parseReadinessArgs(argv) {
86
96
  return { error: "--base-url requires a valid HTTP(S) URL." };
87
97
  parsed.baseUrl = valid.baseUrl;
88
98
  }
99
+ else if (token === "--epic-run-id") {
100
+ const raw = argv[++index];
101
+ if (!raw || raw.startsWith("--") || raw.trim() === "") {
102
+ return { error: "--epic-run-id requires an epic run id." };
103
+ }
104
+ parsed.epicRunId = raw.trim();
105
+ }
89
106
  else
90
107
  return { error: `Unknown argument "${token}". Run "epic-implementer readiness --help" for usage.` };
91
108
  }
92
109
  return parsed;
93
110
  }
111
+ const KNOWN_REVIEW_POLICY_SOURCES = [
112
+ "verdict_protocol",
113
+ "native_review_decision",
114
+ "none",
115
+ "repository_policy",
116
+ ];
117
+ /**
118
+ * Choose the review-policy source the workflow row is judged under (BAPI-1406).
119
+ *
120
+ * `repository_policy` is selected ONLY when a successful, parseable response
121
+ * explicitly confirms the repository's effective review default is unset: the
122
+ * `review_policy` key is present and `null`. A stored default is respected, and
123
+ * an unknown or missing source falls back to `verdict_protocol`. Every other
124
+ * outcome — no access, a read failure, a malformed body, a missing key (an older
125
+ * server), an unrecognised value — returns `verdict_protocol`, so the workflow
126
+ * row stays required rather than falsely healthy.
127
+ *
128
+ * Read-only: one GET through the existing supervisor-setup client. This answers
129
+ * "what would a new feature-branch run's first approval resolve to", which is a
130
+ * prospective assessment and not a prediction or authorization of any run.
131
+ */
132
+ export async function resolveReadinessReviewPolicySource(access, fetchImpl) {
133
+ if (access === null)
134
+ return "verdict_protocol";
135
+ try {
136
+ const setup = await fetchEffectiveSupervisorSetup(access, undefined, fetchImpl);
137
+ if (typeof setup !== "object" || setup === null || Array.isArray(setup)) {
138
+ return "verdict_protocol";
139
+ }
140
+ const record = setup;
141
+ if (!Object.prototype.hasOwnProperty.call(record, "review_policy")) {
142
+ return "verdict_protocol";
143
+ }
144
+ const reviewPolicy = record.review_policy;
145
+ if (reviewPolicy === null)
146
+ return "repository_policy";
147
+ if (typeof reviewPolicy !== "object" || Array.isArray(reviewPolicy)) {
148
+ return "verdict_protocol";
149
+ }
150
+ const source = reviewPolicy.source;
151
+ return KNOWN_REVIEW_POLICY_SOURCES.find((known) => known === source) ?? "verdict_protocol";
152
+ }
153
+ catch {
154
+ return "verdict_protocol";
155
+ }
156
+ }
94
157
  /**
95
158
  * Build the production report for `repoRoot`.
96
159
  *
@@ -98,7 +161,7 @@ export function parseReadinessArgs(argv) {
98
161
  * epic-implementer doctor's local inspections, and the same plane preflight
99
162
  * `plane up` and `drive-epic` run. Nothing is re-implemented here.
100
163
  */
101
- async function collectProductionReport(repoRoot, env, baseUrl) {
164
+ async function collectProductionReport(repoRoot, env, baseUrl, epicRunId) {
102
165
  const { reportEnv, resolverEnv } = readinessEndpointEnvironments(env, baseUrl);
103
166
  const accessResult = baseUrl === undefined
104
167
  ? await resolveConductorBridgeApiAccess({ env: reportEnv, cwd: repoRoot })
@@ -117,6 +180,7 @@ async function collectProductionReport(repoRoot, env, baseUrl) {
117
180
  });
118
181
  }
119
182
  const access = accessResult.ok ? accessResult.access : null;
183
+ const reviewPolicySource = await resolveReadinessReviewPolicySource(access);
120
184
  return collectConductorReadinessGate({
121
185
  // Never guessed: an unresolved identity is reported as such rather than
122
186
  // invented, and the repository ROOT is never placed in the report at all.
@@ -125,10 +189,11 @@ async function collectProductionReport(repoRoot, env, baseUrl) {
125
189
  access,
126
190
  accessError: accessResult.ok ? undefined : accessResult.error,
127
191
  fetch: (...args) => fetch(...args),
128
- // The consolidated gate reports workflow presence as a fact; it selects no
129
- // per-run review policy, so applicability is evaluated under the policy
130
- // that always consumes the workflow's verdict.
131
- reviewPolicySource: "verdict_protocol",
192
+ // BAPI-1406: the workflow row is judged under the review default the
193
+ // server CONFIRMS for a new feature-branch run's first approval, and under
194
+ // `verdict_protocol` whenever that cannot be established. This is a
195
+ // prospective assessment, not a prediction or authorization of any run.
196
+ reviewPolicySource,
132
197
  readWorkflowFile: () => fsReadFile(claudeReviewWorkflowPath(repoRoot), "utf-8"),
133
198
  projectRoot: repoRoot,
134
199
  installDoctorDeps: {
@@ -143,12 +208,19 @@ async function collectProductionReport(repoRoot, env, baseUrl) {
143
208
  },
144
209
  // The deny-enforcement probe is never spawned from this command; the check
145
210
  // is reported as an explicit skip naming `epic-implementer doctor` instead.
146
- conductorDoctorDeps: { env: reportEnv, skipDenyProbe: true },
211
+ conductorDoctorDeps: {
212
+ env: reportEnv,
213
+ skipDenyProbe: true,
214
+ // BAPI-1392: read-only, warn-only committed-command drift row.
215
+ inspectCommandDrift: createCommandDriftInspector({ env: reportEnv, cwd: repoRoot }),
216
+ },
147
217
  // Provided ONLY for a target that names the local plane; otherwise the rows
148
218
  // are skipped without any probe.
149
219
  ...(planeNotApplicable === null
150
220
  ? {
151
221
  runPlanePreflight: () => runDefaultPlanePreflight(repoRoot, reportEnv),
222
+ // BAPI-1397 (F4): only for an applicable local plane.
223
+ ...(epicRunId !== undefined ? { epicRunId } : {}),
152
224
  }
153
225
  : { planeNotApplicable }),
154
226
  });
@@ -175,7 +247,8 @@ export async function runConductorReadinessCommand(argv, deps = {}) {
175
247
  }
176
248
  const cwd = deps.cwd ?? process.cwd();
177
249
  const env = deps.env ?? process.env;
178
- const collect = deps.collect ?? ((repoRoot) => collectProductionReport(repoRoot, env, parsed.baseUrl));
250
+ const collect = deps.collect ??
251
+ ((repoRoot) => collectProductionReport(repoRoot, env, parsed.baseUrl, parsed.epicRunId));
179
252
  const report = await collect(cwd);
180
253
  // Fixed, sanitized, and stderr-only: never a credential, exception, header,
181
254
  // body, subprocess output, or absolute path. It fires on a COLLECTION gap,
@@ -183,7 +256,19 @@ export async function runConductorReadinessCommand(argv, deps = {}) {
183
256
  // every time an ordinary check fails teaches operators to ignore it.
184
257
  if (report.unavailableSources.length > 0)
185
258
  sinks.stderr(READINESS_COLLECTION_NOTICE);
186
- sinks.stdout(parsed.json ? JSON.stringify(report) : formatConductorReadinessGateReport(report));
259
+ if (parsed.json) {
260
+ // BAPI-1397 (F3): the document goes through the shared CLI writer, which waits
261
+ // for a piped stdout to drain. An injected sink is the caller's own boundary,
262
+ // so only the production stdout is flushed. Output completion never becomes a
263
+ // readiness verdict and never changes the advisory exit code.
264
+ await writeJsonDocument(JSON.stringify(report), {
265
+ log: sinks.stdout,
266
+ flush: deps.sinks ? async () => true : flushProcessStdout,
267
+ });
268
+ }
269
+ else {
270
+ sinks.stdout(formatConductorReadinessGateReport(report));
271
+ }
187
272
  // Advisory: the exit code is a constant, never derived from a check status.
188
273
  return 0;
189
274
  }