@bridge_gpt/mcp-server 0.2.54 → 0.2.55

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 (81) hide show
  1. package/README.md +38 -54
  2. package/build/agent-guidance.generated.js +3 -0
  3. package/build/agent-launchers/claude-executor-adapter.js +3 -0
  4. package/build/agent-notes.js +178 -0
  5. package/build/agent-registry.js +5 -2
  6. package/build/agent-utils.js +58 -0
  7. package/build/agents.generated.js +1 -1
  8. package/build/codex-skill-adapter.js +55 -0
  9. package/build/commands.generated.js +5 -4
  10. package/build/conductor/bridge-api-client.js +199 -6
  11. package/build/conductor/bring-up-facts.js +187 -0
  12. package/build/conductor/claude-hook.js +7 -5
  13. package/build/conductor/cli.js +28 -0
  14. package/build/conductor/doctor.js +80 -9
  15. package/build/conductor/epic-implementer-cli.js +1298 -0
  16. package/build/conductor/epic-runtime.js +1 -1
  17. package/build/conductor/errors.js +2 -2
  18. package/build/conductor/git-ci-types.js +1 -1
  19. package/build/conductor/git-hooks.js +28 -14
  20. package/build/conductor/install-doctor.js +11 -5
  21. package/build/conductor/readiness-cli.js +10 -10
  22. package/build/conductor/readiness-sections.js +58 -9
  23. package/build/conductor/readiness.js +37 -6
  24. package/build/conductor/recovery-cli.js +289 -10
  25. package/build/conductor/recovery-operations.js +125 -2
  26. package/build/conductor/repair-contract.js +58 -0
  27. package/build/conductor/store.js +2 -2
  28. package/build/conductor/supervisor-runtime.js +1 -1
  29. package/build/conductor-bin.js +2 -139
  30. package/build/conductor-claude-hook-bin.js +2 -2
  31. package/build/conductor-claude-hook-removed-stub-bin.js +31 -0
  32. package/build/conductor-removed-stub-bin.js +30 -0
  33. package/build/docs.generated.js +1 -1
  34. package/build/doctor.js +77 -17
  35. package/build/drive-epic.js +541 -115
  36. package/build/epic-implementer-bin.js +145 -0
  37. package/build/epic-implementer-bundle-cli.js +264 -0
  38. package/build/epic-implementer-claude-hook-bin.js +3 -0
  39. package/build/epic-integration-pr.js +5 -3
  40. package/build/executor/env.js +6 -0
  41. package/build/executor/interrupted-worktree.js +60 -0
  42. package/build/executor/job-errors.js +45 -0
  43. package/build/executor/job-runner.js +274 -9
  44. package/build/executor/job-types.js +25 -9
  45. package/build/executor/merge-tree-classifier.js +171 -0
  46. package/build/executor/reconcile-overlap-governance.js +129 -0
  47. package/build/executor/reconcile-overlap-job.js +989 -0
  48. package/build/executor/reconcile-overlap-types.js +14 -0
  49. package/build/executor/spawn-job-driver.js +1 -0
  50. package/build/executor/types.js +2 -0
  51. package/build/executor/worker-finalization.js +25 -2
  52. package/build/executor/worker-guard-hook.js +15 -7
  53. package/build/implement-epic/bridge-client.js +773 -0
  54. package/build/implement-epic/checkpoint-store.js +542 -0
  55. package/build/implement-epic/cli.js +3158 -0
  56. package/build/implement-epic/cut-protocol.js +392 -0
  57. package/build/implement-epic/lock.js +302 -0
  58. package/build/implement-epic/pr-state.js +286 -0
  59. package/build/implement-epic/spawn.js +113 -0
  60. package/build/index.js +586 -138
  61. package/build/init.js +72 -8
  62. package/build/install-bridge-conductor.js +5 -5
  63. package/build/install-bridge.js +403 -70
  64. package/build/mcp-host-config.js +22 -60
  65. package/build/mcp-host-entry-adapter.js +18 -0
  66. package/build/mcp-host-targets.js +1 -21
  67. package/build/merge-pull-request.js +1 -1
  68. package/build/pipelines.generated.js +7 -7
  69. package/build/plan-epic-conductor-eligibility.js +1 -1
  70. package/build/plane/cli.js +36 -5
  71. package/build/plane/preflight.js +128 -12
  72. package/build/plane/shutdown.js +4 -4
  73. package/build/readiness-check.js +3 -3
  74. package/build/readme.generated.js +1 -1
  75. package/build/run-unit-tests-launcher.js +1 -1
  76. package/build/setup-epic.js +69 -31
  77. package/build/start-tickets-conductor.js +8 -7
  78. package/build/version.generated.js +3 -3
  79. package/build/worker-guard-hook-bin.js +1 -1
  80. package/docs/CONDUCTOR.md +8 -6
  81. package/package.json +5 -3
@@ -4,7 +4,7 @@
4
4
  * Runs once the epic decomposition's child manifest is frozen and every
5
5
  * sub-task's exploration text is written, so this module can predict — before
6
6
  * any conductor worker is spawned — which children are likely to trip the
7
- * runtime workflow-file merge guard (`api/library/vcs/conductor_merge_service.py`)
7
+ * runtime workflow-file merge guard (`api/library/vcs/epic_implementer_merge_service.py`)
8
8
  * and which of those additionally touch `claude-review.yml` itself, where
9
9
  * automated review cannot run at all (BAPI-941's supply-chain preflight refuses
10
10
  * to review a workflow that differs from the base branch's copy).
@@ -402,7 +402,7 @@ async function runDownAction(sinks, overrides) {
402
402
  * Resolves Bridge access scoped to the MANIFEST's own repository root (never
403
403
  * the process's `cwd`, which is not necessarily the same directory) and calls
404
404
  * the shared `stopEpicRunRecovery` — the identical stop semantics
405
- * `conductor stop-run` uses, so the two surfaces can never drift apart.
405
+ * `epic-implementer stop-run` uses, so the two surfaces can never drift apart.
406
406
  *
407
407
  * Never throws. `shutdownPlane` awaits this directly with no try/catch of its
408
408
  * own, so a credential, transport, or unexpected failure here is converted
@@ -723,6 +723,40 @@ export async function requestPlaneScopedLanes(args) {
723
723
  const result = await requestPlaneManifestLanes(args.repoRoot, args.planeId, args.epicRunId, args.claimScope, createPlaneFsDeps());
724
724
  return result.ok ? { ok: true } : { ok: false, reason: result.reason, message: result.message };
725
725
  }
726
+ /**
727
+ * The detached runtime's own output sinks (BAPI-1153).
728
+ *
729
+ * The runtime's stdout and stderr are pipes to the LAUNCHER, and the launcher is
730
+ * allowed to exit long before the plane does — `drive-epic` returns once its run
731
+ * is created, and a composed bring-up that was refused leaves its control plane
732
+ * running for a later attach. Once the reading end is gone, Node turns the
733
+ * runtime's next write into an asynchronous EPIPE that, unhandled, terminates
734
+ * the process: the plane lost its supervisor at its first event after the
735
+ * launcher left — a lane request served by attach, a member exit, a shutdown
736
+ * signal. The launcher side already refuses to let a broken feed take down a
737
+ * healthy plane; this is the same rule on the runtime side. The feed is
738
+ * advisory — the manifest and the member logs are the durable record — so a
739
+ * write that cannot be delivered is dropped, never fatal.
740
+ */
741
+ function createDetachedRuntimeSinks() {
742
+ for (const stream of [process.stdout, process.stderr]) {
743
+ stream.on("error", () => {
744
+ /* the launcher that was reading this feed has gone; the plane stays up */
745
+ });
746
+ }
747
+ const write = (stream, line) => {
748
+ try {
749
+ stream.write(`${line}\n`);
750
+ }
751
+ catch {
752
+ /* a destroyed feed is not a reason to stop supervising */
753
+ }
754
+ };
755
+ return {
756
+ stdout: (line) => write(process.stdout, line),
757
+ stderr: (line) => write(process.stderr, line),
758
+ };
759
+ }
726
760
  /** The private detached-runtime action. Never part of the documented surface. */
727
761
  async function runRuntimeAction(options, overrides) {
728
762
  const executors = options.executors;
@@ -736,10 +770,7 @@ async function runRuntimeAction(options, overrides) {
736
770
  // cannot assert the refusal at all — which is why the refusal below had never
737
771
  // executed under test and its message string appeared nowhere else in the
738
772
  // repository.
739
- const sinks = overrides.sinks ?? {
740
- stdout: (line) => process.stdout.write(`${line}\n`),
741
- stderr: (line) => process.stderr.write(`${line}\n`),
742
- };
773
+ const sinks = overrides.sinks ?? createDetachedRuntimeSinks();
743
774
  if (typeof planeId !== "string" || planeId.length === 0) {
744
775
  sinks.stderr(`plane runtime refused: ${PLANE_ID_ENV_VAR} was not provided by the launcher.`);
745
776
  return 1;
@@ -15,11 +15,97 @@ import path from "path";
15
15
  import { PLANE_SERVER_PORT_ENV_VAR, planeCheckFinding, planeCheckPassed, } from "./types.js";
16
16
  import { checkPlaneBuildFreshness, checkPlaneRuntimeEntrypoint } from "./build-freshness.js";
17
17
  import { checkAlembicHead } from "./alembic-head.js";
18
- import { manifestHasLiveProcess, readPlaneManifest } from "./manifest.js";
18
+ import { manifestHasLiveProcess, planeManifestLifecycle, probeManifestMembers, readPlaneManifest, } from "./manifest.js";
19
19
  import { detectClaudeLogin, formatClaudeLoginAdvisory } from "../claude-login.js";
20
20
  import { createReadinessCheck, createReadinessCheckSafely, } from "../readiness-check.js";
21
21
  /** Files that must exist for a path to be this repository's root. */
22
22
  const REQUIRED_REPO_FILES = ["main.py", "worker.py", "alembic.ini"];
23
+ /** Phases in which a plane already serving a BOUND run can be re-attached to it. */
24
+ const BOUND_RUN_ATTACHABLE_PHASES = [
25
+ "lanes-starting",
26
+ "lanes-ready",
27
+ "ready",
28
+ ];
29
+ /**
30
+ * Classify a VALID, LIVE manifest found at `repoRoot` for attachment. Pure.
31
+ *
32
+ * The exact-root comparison is the same one `bindPlaneManifestEpicRun` and the
33
+ * guarded lifecycle writers apply (`manifest.repoRoot !== repoRoot`), so a
34
+ * manifest this repository could not bind or request lanes on is never reported
35
+ * attachable.
36
+ */
37
+ export function classifyLivePlaneForAttach(repoRoot, manifest, proc) {
38
+ if (manifest.repoRoot !== repoRoot)
39
+ return { kind: "foreign-root" };
40
+ const lifecycle = planeManifestLifecycle(manifest);
41
+ const probes = probeManifestMembers(manifest, proc);
42
+ const memberAlive = (name) => probes.some((probe) => probe.member.name === name && probe.liveness === "alive");
43
+ if (!memberAlive("server") || !memberAlive("worker")) {
44
+ return { kind: "not-attachable", lifecycle };
45
+ }
46
+ if (lifecycle === "control-plane-ready")
47
+ return { kind: "attachable", manifest };
48
+ if (BOUND_RUN_ATTACHABLE_PHASES.includes(lifecycle) &&
49
+ manifest.epicRunId !== undefined &&
50
+ manifest.laneScope !== undefined) {
51
+ return { kind: "attachable", manifest };
52
+ }
53
+ return { kind: "not-attachable", lifecycle };
54
+ }
55
+ /** Fixed remediation for an occupied port that is not this repository's plane. */
56
+ export const PLANE_PORT_UNRELATED_LISTENER_REMEDIATION = "the listener is not a live plane of this repository — it is often a SIBLING WORKTREE's " +
57
+ "server. Stop it (or wind down its plane from its own worktree) and run drive-epic again.";
58
+ /** Fixed remediation for a live manifest naming a different repository root. */
59
+ export const PLANE_FOREIGN_ROOT_REMEDIATION = "the plane manifest here names a different repository root (copied or symlinked). " +
60
+ "Inspect .bridge/plane/plane.json, wind that plane down from its own repository, and run " +
61
+ "drive-epic again.";
62
+ /** Fixed remediation for this repository's live plane that cannot take a run. */
63
+ export const PLANE_NOT_ATTACHABLE_REMEDIATION = "this repository's plane is running but cannot take a new run — it is still starting, its " +
64
+ "server or reconciler is gone, its lanes failed, or its lanes were started by hand with " +
65
+ "their own claim scope. Inspect it with `plane status`; wait for it to finish starting, or " +
66
+ "wind it down with `plane down` and run drive-epic again to cold-start.";
67
+ /** Fixed remediation when this repository's attachable plane was not attached to. */
68
+ export const PLANE_ATTACHABLE_BASE_URL_REMEDIATION = `do not stop it — point BAPI_BASE_URL at the plane endpoint (http://127.0.0.1:<port>, the port ` +
69
+ `${PLANE_SERVER_PORT_ENV_VAR} or the default, with no trailing slash) and run drive-epic again to attach.`;
70
+ /**
71
+ * Describe what occupies the plane port, for a single operator refusal. Pure.
72
+ *
73
+ * Every non-attachable occupant keeps its own classification and fix, but they
74
+ * all render as ONE refusal: the operator has one thing to do, and it depends on
75
+ * which of these it is.
76
+ */
77
+ export function describePlanePortOccupant(observation) {
78
+ switch (observation?.kind) {
79
+ case "unvalidated":
80
+ return {
81
+ classification: "an existing plane manifest could not be validated",
82
+ remediation: PLANE_MANIFEST_UNREADABLE_REMEDIATION,
83
+ };
84
+ case "foreign-root":
85
+ return {
86
+ classification: "a live plane manifest names a different repository root",
87
+ remediation: PLANE_FOREIGN_ROOT_REMEDIATION,
88
+ };
89
+ case "not-attachable":
90
+ return {
91
+ classification: `this repository's plane is live but not attachable (lifecycle: ${observation.lifecycle})`,
92
+ remediation: PLANE_NOT_ATTACHABLE_REMEDIATION,
93
+ };
94
+ case "attachable":
95
+ // Reached only when the caller declined to attach — the Bridge API it
96
+ // talks to is not this plane. Telling the operator to stop the listener
97
+ // would stop a healthy plane; the fix is the base URL.
98
+ return {
99
+ classification: "this repository's own healthy plane holds the port, but BAPI_BASE_URL does not name it",
100
+ remediation: PLANE_ATTACHABLE_BASE_URL_REMEDIATION,
101
+ };
102
+ default:
103
+ return {
104
+ classification: "the port is held by a listener that is not a live plane of this repository",
105
+ remediation: PLANE_PORT_UNRELATED_LISTENER_REMEDIATION,
106
+ };
107
+ }
108
+ }
23
109
  /** Fixed remediation for a `plane up` run started outside the repository root. */
24
110
  export const PLANE_REPOSITORY_ROOT_REMEDIATION = "run `plane up` from the Bridge API repository root (the directory holding main.py, worker.py, and alembic.ini).";
25
111
  /** Fixed remediation for an unresolvable Bridge repository identity. */
@@ -148,8 +234,11 @@ export async function runPlanePreflight(repoRoot, deps) {
148
234
  // An unresolvable override is a configuration failure, not a port failure:
149
235
  // there is no port to probe, so the probe is skipped entirely rather than
150
236
  // falling back to 8000 and reporting on a port the operator did not ask for.
237
+ const observations = {};
151
238
  if (deps.endpoint.ok) {
152
- record(await checkServerPort(deps, deps.endpoint.endpoint));
239
+ const port = await checkServerPort(deps, deps.endpoint.endpoint);
240
+ record(port);
241
+ observations.serverPort = port.probe;
153
242
  }
154
243
  else {
155
244
  // The endpoint resolver owns this diagnostic's prose; the structured fix is
@@ -166,7 +255,9 @@ export async function runPlanePreflight(repoRoot, deps) {
166
255
  fileExists: (filePath) => fileExists(filePath, deps.fs),
167
256
  platform: deps.platform,
168
257
  }));
169
- record(await checkExistingPlane(repoRoot, deps));
258
+ const existing = await checkExistingPlane(repoRoot, deps);
259
+ record(existing);
260
+ observations.existingPlane = existing.observation;
170
261
  const blocking = diagnostics.filter((d) => d.severity === "blocking");
171
262
  // `!runtimeEntrypoint.ok` is already covered by the blocking count; it is
172
263
  // repeated here so the compiler narrows the union rather than requiring a
@@ -175,12 +266,13 @@ export async function runPlanePreflight(repoRoot, deps) {
175
266
  // here, like `!runtimeEntrypoint.ok`, so the compiler narrows the union rather
176
267
  // than requiring a non-null assertion on the context field below.
177
268
  if (blocking.length > 0 || !credentials.ok || !runtimeEntrypoint.ok || !deps.endpoint.ok) {
178
- return { ok: false, diagnostics, outcomes };
269
+ return { ok: false, diagnostics, outcomes, observations };
179
270
  }
180
271
  return {
181
272
  ok: true,
182
273
  diagnostics,
183
274
  outcomes,
275
+ observations,
184
276
  context: {
185
277
  repoRoot,
186
278
  repoName: credentials.repoName,
@@ -559,16 +651,22 @@ function createResolverWarningBuffer() {
559
651
  * refuse a perfectly good bring-up.
560
652
  */
561
653
  export async function checkServerPort(deps, endpoint) {
654
+ // BAPI-1153: the probe's own result kind rides beside the unchanged report, so
655
+ // a composing caller reads what was observed instead of re-probing the port.
656
+ const report = await checkServerPortReport(deps, endpoint);
657
+ return { ...report.report, probe: report.probe };
658
+ }
659
+ async function checkServerPortReport(deps, endpoint) {
562
660
  const label = "Local server port";
563
661
  const target = `${endpoint.host}:${endpoint.port}`;
564
662
  const result = await deps.probePort(endpoint.host, endpoint.port, PLANE_PORT_PROBE_TIMEOUT_MS);
565
663
  if (result.kind === "refused") {
566
- return planeCheckPassed("server-port", label, `${target} is free`);
664
+ return { report: planeCheckPassed("server-port", label, `${target} is free`), probe: "refused" };
567
665
  }
568
666
  // Severity is unchanged in both branches: `connected` still blocks, an
569
667
  // indeterminate probe still only warns.
570
668
  if (result.kind === "connected") {
571
- return planeCheckFinding({
669
+ const occupied = planeCheckFinding({
572
670
  check: "server-port",
573
671
  severity: "blocking",
574
672
  message: `${target} is already accepting connections. ` +
@@ -577,8 +675,9 @@ export async function checkServerPort(deps, endpoint) {
577
675
  "to a free port.",
578
676
  remediation: PLANE_SERVER_PORT_OCCUPIED_REMEDIATION,
579
677
  }, label, `${target} is already accepting connections`);
678
+ return { report: occupied, probe: "connected" };
580
679
  }
581
- return planeCheckFinding({
680
+ const indeterminate = planeCheckFinding({
582
681
  check: "server-port",
583
682
  severity: "warning",
584
683
  message: `could not determine whether ${target} is free ` +
@@ -588,6 +687,7 @@ export async function checkServerPort(deps, endpoint) {
588
687
  // The probe's own `error` text stays in the legacy message for the operator
589
688
  // at the terminal and is not copied into the consolidated detail.
590
689
  `could not determine whether ${target} is free`);
690
+ return { report: indeterminate, probe: "error" };
591
691
  }
592
692
  /**
593
693
  * Refuse to start over a plane that is still alive.
@@ -602,11 +702,18 @@ export async function checkExistingPlane(repoRoot, deps) {
602
702
  // Three distinct states, three distinct fixes (BAPI-1055): unreadable/malformed,
603
703
  // stale, and live. Manifest validation, the liveness probe, and the
604
704
  // never-touch-disk rule are all unchanged.
705
+ //
706
+ // BAPI-1153: each arm also returns its ATTACH classification. The diagnostics
707
+ // are untouched — a live plane still blocks `plane up` — and the classification
708
+ // is read from this one manifest read, never a second one.
605
709
  if (read.kind === "missing") {
606
- return planeCheckPassed("existing-plane", label, "no plane manifest is present");
710
+ return {
711
+ ...planeCheckPassed("existing-plane", label, "no plane manifest is present"),
712
+ observation: { kind: "none" },
713
+ };
607
714
  }
608
715
  if (read.kind !== "valid") {
609
- return planeCheckFinding({
716
+ const unvalidated = planeCheckFinding({
610
717
  check: "existing-plane",
611
718
  severity: "blocking",
612
719
  message: `an existing .bridge/plane/plane.json could not be validated (${read.error}). ` +
@@ -614,6 +721,7 @@ export async function checkExistingPlane(repoRoot, deps) {
614
721
  "it by hand if no plane is running.",
615
722
  remediation: PLANE_MANIFEST_UNREADABLE_REMEDIATION,
616
723
  }, label, "an existing plane manifest could not be validated");
724
+ return { ...unvalidated, observation: { kind: "unvalidated" } };
617
725
  }
618
726
  // BAPI-882: a manifest carrying THIS process's own plane id is its own claim,
619
727
  // not a competitor. Checked after validation and BEFORE the liveness probe,
@@ -632,13 +740,17 @@ export async function checkExistingPlane(repoRoot, deps) {
632
740
  // A live manifest with a DIFFERENT plane id still blocks, unchanged — that is
633
741
  // a genuine second plane and the whole point of the check.
634
742
  if (deps.ownPlaneId !== undefined && read.manifest.planeId === deps.ownPlaneId) {
635
- return planeCheckPassed("existing-plane", label, "the existing manifest carries this launch's own plane id");
743
+ return {
744
+ ...planeCheckPassed("existing-plane", label, "the existing manifest carries this launch's own plane id"),
745
+ observation: { kind: "own-plane" },
746
+ };
636
747
  }
637
748
  if (!manifestHasLiveProcess(read.manifest, deps.proc)) {
638
749
  // Stale. Reported as information so the operator understands why an old
639
750
  // manifest is about to be replaced; the claim revalidates before it does.
640
751
  // No remediation: the operator has nothing to do — the claim replaces it.
641
752
  return {
753
+ observation: { kind: "stale" },
642
754
  diagnostic: {
643
755
  check: "existing-plane",
644
756
  severity: "warning",
@@ -656,7 +768,7 @@ export async function checkExistingPlane(repoRoot, deps) {
656
768
  ],
657
769
  };
658
770
  }
659
- return planeCheckFinding({
771
+ const running = planeCheckFinding({
660
772
  check: "existing-plane",
661
773
  severity: "blocking",
662
774
  message: `a plane is already running (supervisor pid ${read.manifest.supervisorPid}). ` +
@@ -666,6 +778,10 @@ export async function checkExistingPlane(repoRoot, deps) {
666
778
  // The supervisor pid stays in the legacy message; the consolidated detail
667
779
  // reports the fact, not the process identifier.
668
780
  "a plane is already running for this repository");
781
+ return {
782
+ ...running,
783
+ observation: classifyLivePlaneForAttach(repoRoot, read.manifest, deps.proc),
784
+ };
669
785
  }
670
786
  async function fileExists(filePath, fs) {
671
787
  try {
@@ -702,7 +818,7 @@ export const PLANE_READINESS_DESCRIPTORS = [
702
818
  /** Fixed detail for a plane prerequisite the preflight never reported on. */
703
819
  const PLANE_UNREPORTED_DETAIL = "not reported — the plane preflight produced no outcome for this prerequisite";
704
820
  /** Fixed remediation for a plane prerequisite the preflight never reported on. */
705
- const PLANE_UNREPORTED_REMEDIATION = "run `plane up` (or `conductor readiness` again) to re-collect the plane preflight; this " +
821
+ const PLANE_UNREPORTED_REMEDIATION = "run `plane up` (or `epic-implementer readiness` again) to re-collect the plane preflight; this " +
706
822
  "prerequisite's state is unknown, not healthy.";
707
823
  /**
708
824
  * Project a plane preflight result into canonical readiness checks.
@@ -230,7 +230,7 @@ async function waitForDeath(manifest, deps, budgetMs, pollMs) {
230
230
  /**
231
231
  * Render the server-side run-shutdown outcome (BAPI-872) as its own message
232
232
  * lines — never claims signalling local processes changed server-side state,
233
- * and always names the supported `conductor stop-run` check/remediation
233
+ * and always names the supported `epic-implementer stop-run` check/remediation
234
234
  * surface for every outcome that is not a fresh committed stop.
235
235
  */
236
236
  function formatRunShutdown(outcome) {
@@ -239,7 +239,7 @@ function formatRunShutdown(outcome) {
239
239
  return [
240
240
  "Run shutdown: no epic run is bound to this plane. If a server-side run is " +
241
241
  "active, this command did NOT stop it and its queued work may remain live. " +
242
- "Run `conductor stop-run --epic-run-id <epic_run_id>` to stop it explicitly.",
242
+ "Run `epic-implementer stop-run --epic-run-id <epic_run_id>` to stop it explicitly.",
243
243
  ];
244
244
  case "committed":
245
245
  return [`Run shutdown: stopped run ${outcome.epicRunId} — its queued work was cancelled.`];
@@ -249,13 +249,13 @@ function formatRunShutdown(outcome) {
249
249
  return [
250
250
  `Run shutdown: run ${outcome.epicRunId} is terminal (${outcome.status}) and cannot be ` +
251
251
  "stopped. Signalling local processes does not change this — run " +
252
- `\`conductor stop-run --epic-run-id ${outcome.epicRunId}\` to check its state.`,
252
+ `\`epic-implementer stop-run --epic-run-id ${outcome.epicRunId}\` to check its state.`,
253
253
  ];
254
254
  case "unavailable":
255
255
  return [
256
256
  `Run shutdown: could not reach the Bridge API to stop run ${outcome.epicRunId} ` +
257
257
  `(${outcome.message}). The run may remain active with queued work — run ` +
258
- `\`conductor stop-run --epic-run-id ${outcome.epicRunId}\` to retry.`,
258
+ `\`epic-implementer stop-run --epic-run-id ${outcome.epicRunId}\` to retry.`,
259
259
  ];
260
260
  }
261
261
  }
@@ -2,7 +2,7 @@
2
2
  * The canonical shared readiness-check contract (BAPI-1055, AC-6).
3
3
  *
4
4
  * Conductor prerequisite probing is spread across five loci — `bridge doctor`
5
- * ({@link file:./install-doctor.ts}), `conductor doctor`
5
+ * ({@link file:./install-doctor.ts}), `epic-implementer doctor`
6
6
  * ({@link file:./conductor/doctor.ts}), `plane` preflight
7
7
  * ({@link file:./plane/preflight.ts}), the server `conductor-readiness`
8
8
  * collector, and the unified install doctor
@@ -277,7 +277,7 @@ export function createReadinessCheck(input) {
277
277
  /** Fixed prose for a check that had to be replaced because it was malformed. */
278
278
  export const READINESS_MALFORMED_CHECK_DETAIL = "this prerequisite reported a result that failed the readiness contract, so its real state is unknown";
279
279
  /** Fixed remediation for a replaced malformed check. */
280
- export const READINESS_MALFORMED_CHECK_REMEDIATION = "re-run `conductor readiness`; if it persists, run `conductor doctor` and `install conductor` to re-collect this locus.";
280
+ export const READINESS_MALFORMED_CHECK_REMEDIATION = "re-run `epic-implementer readiness`; if it persists, run `epic-implementer doctor` and `install conductor` to re-collect this locus.";
281
281
  /** Fixed label for a replaced malformed check. */
282
282
  export const READINESS_MALFORMED_CHECK_LABEL = "Unreadable prerequisite";
283
283
  /**
@@ -330,7 +330,7 @@ export function summarizeReadinessChecks(checks) {
330
330
  /** Fixed stand-in for a source detail that failed the secret-safety contract. */
331
331
  export const READINESS_UNSAFE_DETAIL_REPLACEMENT = "detail withheld — the source reported it in a form this report may not carry";
332
332
  /** Fixed stand-in for a source remediation that failed the secret-safety contract. */
333
- export const READINESS_UNSAFE_REMEDIATION_REPLACEMENT = "re-run `conductor readiness`; this prerequisite's own fix text could not be carried into this report safely.";
333
+ export const READINESS_UNSAFE_REMEDIATION_REPLACEMENT = "re-run `epic-implementer readiness`; this prerequisite's own fix text could not be carried into this report safely.";
334
334
  /**
335
335
  * Build a check, degrading REJECTED prose rather than losing the finding — and
336
336
  * never throwing.