@bridge_gpt/mcp-server 0.2.57 → 0.2.59

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 (90) hide show
  1. package/README.md +112 -113
  2. package/build/agent-registry.js +15 -0
  3. package/build/agent-utils.js +16 -8
  4. package/build/claude-user-config-doctor.js +1 -35
  5. package/build/codex-bridge-profile.js +5 -1
  6. package/build/codex-launch-scope.js +13 -2
  7. package/build/commands.generated.js +5 -6
  8. package/build/conductor/bridge-api-client.js +149 -0
  9. package/build/conductor/bring-up-facts.js +1 -1
  10. package/build/conductor/claude-hook.js +16 -16
  11. package/build/conductor/doctor.js +24 -19
  12. package/build/conductor/epic-implementer-cli.js +38 -5
  13. package/build/conductor/epic-reconcile.js +1 -1
  14. package/build/conductor/epic-runtime.js +10 -10
  15. package/build/conductor/errors.js +3 -3
  16. package/build/conductor/file-scope-guard.js +4 -4
  17. package/build/conductor/git-ci-types.js +1 -1
  18. package/build/conductor/git-inspection.js +2 -2
  19. package/build/conductor/install-doctor.js +6 -6
  20. package/build/conductor/merge-ledger.js +2 -2
  21. package/build/conductor/pr-ci-producer.js +6 -6
  22. package/build/conductor/pr-review-producer.js +2 -2
  23. package/build/conductor/producer-ledger.js +2 -0
  24. package/build/conductor/readiness-sections.js +7 -7
  25. package/build/conductor/readiness.js +2 -2
  26. package/build/conductor/recovery-cli.js +243 -12
  27. package/build/conductor/recovery-operations.js +56 -4
  28. package/build/conductor/repair-contract.js +38 -4
  29. package/build/conductor/spec-review-producer.js +6 -6
  30. package/build/conductor/store.js +10 -10
  31. package/build/conductor/supervisor-config.js +14 -14
  32. package/build/conductor/supervisor-ledger.js +2 -2
  33. package/build/conductor/supervisor-message-relay.js +2 -2
  34. package/build/conductor/worker-ledger-cli.js +19 -19
  35. package/build/conductor-bundle-artifacts.js +2 -2
  36. package/build/cursor-agent-progress.js +72 -0
  37. package/build/doctor.js +20 -296
  38. package/build/drive-epic.js +65 -71
  39. package/build/epic-implementer-bin.js +22 -17
  40. package/build/epic-implementer-claude-hook-bin.js +4 -2
  41. package/build/epic-implementer-env.js +155 -0
  42. package/build/epic-integration-pr.js +2 -2
  43. package/build/epic-shared/bridge-client.js +773 -0
  44. package/build/epic-shared/cut-protocol.js +392 -0
  45. package/build/executor/env.js +19 -8
  46. package/build/executor/heartbeat.js +1 -1
  47. package/build/executor/job-errors.js +3 -3
  48. package/build/executor/job-runner.js +1 -1
  49. package/build/executor/merge-job.js +35 -3
  50. package/build/executor/observation.js +1 -1
  51. package/build/executor/preflight.js +1 -1
  52. package/build/executor/prompt-spec.js +1 -1
  53. package/build/executor/reconcile-overlap-job.js +10 -130
  54. package/build/executor/reconcile-overlap-types.js +1 -1
  55. package/build/executor/runner.js +1 -1
  56. package/build/implement-epic/cli.js +1 -1
  57. package/build/index.js +187 -224
  58. package/build/init.js +20 -28
  59. package/build/install-bridge-conductor.js +7 -7
  60. package/build/install-bridge.js +203 -10
  61. package/build/install-doctor.js +1 -1
  62. package/build/mcp-host-entry-adapter.js +6 -0
  63. package/build/mcp-host-targets.js +2 -0
  64. package/build/mcp-invoke.js +5 -5
  65. package/build/mcp-profile.js +2 -2
  66. package/build/mcp-provisioning.js +2 -2
  67. package/build/mcp-server-invocation.js +3 -3
  68. package/build/merge-pull-request.js +15 -0
  69. package/build/model-routing-host.js +25 -0
  70. package/build/model-routing-preferences.js +99 -0
  71. package/build/pipelines.generated.js +11 -11
  72. package/build/plan-epic-implementer-eligibility.js +215 -0
  73. package/build/plan-execution-ledger.js +8 -0
  74. package/build/plane/member-roster.js +5 -5
  75. package/build/plane/supervisor.js +1 -1
  76. package/build/plane/types.js +5 -5
  77. package/build/readiness-check.js +1 -1
  78. package/build/readme.generated.js +1 -1
  79. package/build/review-tickets.js +4 -4
  80. package/build/setup-epic.js +8 -8
  81. package/build/start-tickets-conductor.js +32 -32
  82. package/build/start-tickets-prereqs.js +4 -9
  83. package/build/start-tickets.js +494 -98
  84. package/build/version.generated.js +3 -3
  85. package/docs/CONDUCTOR.md +69 -18
  86. package/docs/install/mcp-tool-integrations.md +2 -1
  87. package/package.json +2 -2
  88. package/pipelines/implement-ticket.json +2 -2
  89. package/pipelines/plan-epic.json +2 -2
  90. package/pipelines/review-ticket.json +1 -1
@@ -17,8 +17,6 @@
17
17
  * worker-completed ONLY when the merge conflicted or the checks were red:
18
18
  * exactly one worker, holding both tickets' intents, edits
19
19
  * the working tree — it never commits and never pushes;
20
- * governance-scanned the candidate is compared against the governance regions
21
- * the server read at the INPUT epic head;
22
20
  * pushed one fast-forward push, leased on the recorded child head;
23
21
  * cleaned every exit that does not push resets the worktree to the
24
22
  * recorded child head.
@@ -32,7 +30,7 @@
32
30
  * The job returns FACTS, never a verdict (R14 rule 3): the input heads, the
33
31
  * classification, whether a worker ran, every edited path and why, the local
34
32
  * checks observed, and either the pushed head or the reason nothing was pushed —
35
- * a governance fact or an observed moved head. The server verifies all of it
33
+ * an observed moved head. The server verifies all of it
36
34
  * against the provider (`reconcile_overlap_completion_contract.py`).
37
35
  *
38
36
  * Runtime prerequisite: the push runs in the executor's OWN process under the
@@ -47,7 +45,6 @@ import { allRequiredChecksGreen } from "../conductor/local-merge.js";
47
45
  import { buildExecutorBaseWorkerEnv } from "./env.js";
48
46
  import { ReconcileOverlapBranchMismatch, ReconcileOverlapConflictsUnresolved, ReconcileOverlapMergeFailed, ReconcileOverlapNothingToMerge, ReconcileOverlapPayload as ReconcileOverlapPayloadInvalid, ReconcileOverlapPushRejected, ReconcileOverlapPushUnauthorized, ReconcileOverlapTopologyRefused, ReconcileOverlapVerificationRed, ReconcileOverlapWorkerRewroteHistory, } from "./job-errors.js";
49
47
  import { classifyMergeTree } from "./merge-tree-classifier.js";
50
- import { scanGovernanceRegions } from "./reconcile-overlap-governance.js";
51
48
  import { RECONCILE_OVERLAP_PAYLOAD_SCHEMA_VERSION, } from "./reconcile-overlap-types.js";
52
49
  import { validateWorkerFinalization } from "./worker-finalization.js";
53
50
  export const RECONCILE_OVERLAP_JOB_TYPE = "reconcile_overlap";
@@ -65,7 +62,6 @@ const MAX_ARGV = 32;
65
62
  const MAX_ARG_CHARS = 1024;
66
63
  const MAX_COMMAND_TIMEOUT_SECONDS = 30 * 60;
67
64
  const MAX_CHANGED_FILES = 1000;
68
- const MAX_GOVERNANCE_REGIONS = 64;
69
65
  const REF_NAME_MAX_CHARS = 255;
70
66
  const CONFLICT_REPORT_DIR = ".conductor/";
71
67
  /** The executor's per-worktree artifact directory, never part of a candidate. */
@@ -92,8 +88,6 @@ const WRAPPER_STAGE_EXCLUDES = [
92
88
  ":(exclude).claude/settings.local.json",
93
89
  ];
94
90
  const SHA_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
95
- const TAG_RE = /^[A-Z][A-Z0-9_]{0,31}$/;
96
- const SHA256_HEX_RE = /^[0-9a-f]{64}$/;
97
91
  const FAILED_CHECK_CONCLUSIONS = new Set([
98
92
  "failure",
99
93
  "cancelled",
@@ -194,54 +188,6 @@ function ticketContext(value, field) {
194
188
  verificationCommands: verificationCommands(entry.verification_commands, `${field}.verification_commands`),
195
189
  };
196
190
  }
197
- function governanceRegions(value) {
198
- if (!Array.isArray(value))
199
- throw new PayloadError("governance.regions must be a list");
200
- if (value.length > MAX_GOVERNANCE_REGIONS)
201
- throw new PayloadError("governance.regions exceeds its bound");
202
- return value.map((raw, index) => {
203
- const entry = asRecord(raw, `governance.regions[${index}]`);
204
- const tag = typeof entry.tag === "string" ? entry.tag : "";
205
- const regionPath = normalizeRepoRelativePath(entry.path);
206
- const begin = entry.begin_marker;
207
- const end = entry.end_marker;
208
- const sha = entry.content_sha256;
209
- if (!TAG_RE.test(tag) ||
210
- regionPath === null ||
211
- typeof begin !== "string" ||
212
- typeof end !== "string" ||
213
- !begin.includes(tag) ||
214
- !end.includes(tag) ||
215
- typeof sha !== "string" ||
216
- !SHA256_HEX_RE.test(sha) ||
217
- !Number.isInteger(entry.begin_line) ||
218
- !Number.isInteger(entry.end_line)) {
219
- throw new PayloadError(`governance.regions[${index}] is malformed`);
220
- }
221
- return {
222
- tag,
223
- path: regionPath,
224
- beginLine: entry.begin_line,
225
- endLine: entry.end_line,
226
- beginMarker: begin,
227
- endMarker: end,
228
- contentSha256: sha,
229
- };
230
- });
231
- }
232
- function governanceFindings(value) {
233
- if (value === undefined || value === null)
234
- return [];
235
- if (!Array.isArray(value))
236
- throw new PayloadError("governance.findings must be a list");
237
- return value.map((raw, index) => {
238
- const entry = asRecord(raw, `governance.findings[${index}]`);
239
- const findingPath = normalizeRepoRelativePath(entry.path);
240
- if (findingPath === null)
241
- throw new PayloadError(`governance.findings[${index}].path is unsafe`);
242
- return { path: findingPath, reason: typeof entry.reason === "string" ? entry.reason.slice(0, 300) : "" };
243
- });
244
- }
245
191
  /**
246
192
  * Validate the claimed job's payload. Pure — no subprocess, no filesystem. A
247
193
  * payload missing either input SHA fails here, so the job never infers a head.
@@ -270,7 +216,9 @@ export function validateReconcileOverlapPayload(job) {
270
216
  .map((c) => c.trim());
271
217
  const child = ticketContext(payload.child, "payload.child");
272
218
  const peer = payload.peer === null || payload.peer === undefined ? null : ticketContext(payload.peer, "payload.peer");
273
- const governanceRaw = asRecord(payload.governance, "payload.governance");
219
+ // BAPI-1215 — `payload.governance` is retired. A server that predates the
220
+ // retirement still sends it: it is accepted when present and never read, so
221
+ // this executor consumes both payload shapes at schema version 1.
274
222
  const conflictReportPath = normalizeRepoRelativePath(payload.conflict_report_path);
275
223
  if (conflictReportPath === null || !conflictReportPath.startsWith(CONFLICT_REPORT_DIR)) {
276
224
  throw new PayloadError("payload.conflict_report_path must name a file under .conductor/");
@@ -295,11 +243,6 @@ export function validateReconcileOverlapPayload(job) {
295
243
  requiredChecks,
296
244
  child,
297
245
  peer,
298
- governance: {
299
- regions: governanceRegions(governanceRaw.regions ?? []),
300
- findings: governanceFindings(governanceRaw.findings),
301
- scannedPaths: pathList(governanceRaw.scanned_paths ?? [], "governance.scanned_paths", MAX_CHANGED_FILES * 2),
302
- },
303
246
  declaredTouchedFilesUnion: pathList(payload.declared_touched_files_union ?? [], "payload.declared_touched_files_union", MAX_CHANGED_FILES * 4),
304
247
  conflictReportPath,
305
248
  prompt,
@@ -566,55 +509,6 @@ export function createReconcileOverlapDriver(job, payload, seams) {
566
509
  }
567
510
  return runs;
568
511
  };
569
- /** Paths the candidate changes relative to the input epic head. */
570
- const changedPathsAgainstEpic = async (ctx, candidate) => {
571
- const r = await git(ctx, ["diff", "--name-only", "-z", "--no-renames", payload.requiredEpicSha, candidate]);
572
- if (r.exitCode !== 0)
573
- return null;
574
- return r.stdout
575
- .split("\0")
576
- .map((p) => normalizeRepoRelativePath(p))
577
- .filter((p) => p !== null);
578
- };
579
- const readTreeFile = async (ctx, treeish, filePath) => {
580
- const r = await git(ctx, ["cat-file", "blob", `${treeish}:${filePath}`]);
581
- if (r.exitCode !== 0) {
582
- const exists = await git(ctx, ["cat-file", "-e", `${treeish}:${filePath}`]);
583
- return exists.exitCode === 0 ? { kind: "unreadable" } : { kind: "absent" };
584
- }
585
- return r.stdout.includes("\0") ? { kind: "binary" } : { kind: "text", text: r.stdout };
586
- };
587
- /** Governance scan of a candidate tree or commit against the input epic head. */
588
- const scanCandidate = async (ctx, candidate) => {
589
- const changed = await changedPathsAgainstEpic(ctx, candidate);
590
- if (changed === null)
591
- return "unprovable";
592
- return scanGovernanceRegions({
593
- snapshot: payload.governance,
594
- changedPaths: changed,
595
- readCandidate: (p) => readTreeFile(ctx, candidate, p),
596
- readInput: (p) => readTreeFile(ctx, payload.requiredEpicSha, p),
597
- });
598
- };
599
- const unprovableFact = () => ({
600
- tag: "GOVERNANCE_MARKERS_UNPROVABLE",
601
- paths: [],
602
- findings: [{ tag: "GOVERNANCE_MARKERS_UNPROVABLE", paths: [] }],
603
- truncated: false,
604
- });
605
- /** `git write-tree` of the index: the candidate BEFORE a commit exists. */
606
- const indexTree = async (ctx) => {
607
- const r = await git(ctx, ["write-tree"]);
608
- const oid = r.stdout.trim().toLowerCase();
609
- return r.exitCode === 0 && SHA_RE.test(oid) ? oid : null;
610
- };
611
- const governanceGate = async (ctx, candidate) => {
612
- const scanned = candidate === null ? "unprovable" : await scanCandidate(ctx, candidate);
613
- if (scanned === null)
614
- return null;
615
- await resetToChildHead(ctx);
616
- return complete({ governance_guard_change_required: scanned === "unprovable" ? unprovableFact() : scanned });
617
- };
618
512
  const commitStaged = async (ctx, message) => {
619
513
  const r = await git(ctx, ["commit", "--no-verify", "--no-edit", "-m", message]);
620
514
  if (r.exitCode !== 0)
@@ -678,12 +572,12 @@ export function createReconcileOverlapDriver(job, payload, seams) {
678
572
  await seams.sleep(Math.min(CI_WAIT_POLL_INTERVAL_MS, Math.max(0, ctx.remainingMs() - CI_WAIT_MARGIN_MS)));
679
573
  }
680
574
  };
681
- /** Drift check, final governance scan, guarded push, finalization, CI wait. */
575
+ /** Drift check, guarded push, finalization, CI wait. */
682
576
  const pushFlow = async (ctx) => {
683
577
  const candidate = await revParse(ctx, "HEAD");
684
578
  if (candidate === null) {
685
579
  await resetToChildHead(ctx);
686
- return fail(ReconcileOverlapTopologyRefused, "governance-scanned", "the candidate head could not be read");
580
+ return fail(ReconcileOverlapTopologyRefused, "pushed", "the candidate head could not be read");
687
581
  }
688
582
  // (1) Freshness: both remote heads must still be the dispatched ones. A moved
689
583
  // head is an OBSERVATION the server re-dispatches on; it is not a failure.
@@ -708,11 +602,7 @@ export function createReconcileOverlapDriver(job, payload, seams) {
708
602
  }
709
603
  return complete({ observed_epic_head: heads.epic });
710
604
  }
711
- // (2) Final governance scan of the exact tree about to be pushed.
712
- const gated = await governanceGate(ctx, candidate);
713
- if (gated !== null)
714
- return gated;
715
- // (3) The ONE push: a fast-forward of the child branch, leased on the
605
+ // (2) The ONE push: a fast-forward of the child branch, leased on the
716
606
  // recorded child head. Never plain --force, never a lease-less lease.
717
607
  const destination = `refs/heads/${payload.expectedBranch}`;
718
608
  const push = await git(ctx, [
@@ -741,7 +631,7 @@ export function createReconcileOverlapDriver(job, payload, seams) {
741
631
  }
742
632
  return fail(ReconcileOverlapPushRejected, "pushed", "the remote refused the guarded push");
743
633
  }
744
- // (4) Finalization runs ONCE, after the wrapper's push, against the pushed
634
+ // (3) Finalization runs ONCE, after the wrapper's push, against the pushed
745
635
  // head: origin must carry it and the PR must be attached at it.
746
636
  const finalize = seams.finalize ?? validateWorkerFinalization;
747
637
  const finalization = await finalize({
@@ -758,7 +648,7 @@ export function createReconcileOverlapDriver(job, payload, seams) {
758
648
  });
759
649
  if (!finalization.ok)
760
650
  return { kind: "fail", failure: finalization.failure };
761
- // (5) Wait for provider checks at the pushed head, bounded by the job budget.
651
+ // (4) Wait for provider checks at the pushed head, bounded by the job budget.
762
652
  await waitForProviderChecks(ctx, candidate);
763
653
  return complete({ resulting_head: candidate }, candidate);
764
654
  };
@@ -835,10 +725,6 @@ export function createReconcileOverlapDriver(job, payload, seams) {
835
725
  declaredTouchedFiles: payload.declaredTouchedFilesUnion,
836
726
  };
837
727
  }
838
- // Clean: scan the merged index BEFORE the wrapper creates the merge commit.
839
- const gated = await governanceGate(ctx, await indexTree(ctx));
840
- if (gated !== null)
841
- return finish(gated);
842
728
  const mergeCommit = await commitStaged(ctx, `Reconcile overlap: merge ${payload.requiredEpicSha.slice(0, 12)} into ${payload.expectedBranch}`);
843
729
  if (mergeCommit === null) {
844
730
  await resetToChildHead(ctx);
@@ -900,9 +786,6 @@ export function createReconcileOverlapDriver(job, payload, seams) {
900
786
  await resetToChildHead(ctx);
901
787
  return fail(ReconcileOverlapConflictsUnresolved, "worker-completed", "unmerged index entries remain");
902
788
  }
903
- const gated = await governanceGate(ctx, await indexTree(ctx));
904
- if (gated !== null)
905
- return gated;
906
789
  const mergeCommit = await commitStaged(ctx, `Reconcile overlap: merge ${payload.requiredEpicSha.slice(0, 12)} into ${payload.expectedBranch}`);
907
790
  if (mergeCommit === null) {
908
791
  await resetToChildHead(ctx);
@@ -913,9 +796,6 @@ export function createReconcileOverlapDriver(job, payload, seams) {
913
796
  // Any remaining worker repair: one ordinary commit ON TOP of the merge.
914
797
  await git(ctx, ["add", "-A", "--", ".", ...WRAPPER_STAGE_EXCLUDES]);
915
798
  if (await stagedChanges(ctx)) {
916
- const gated = await governanceGate(ctx, await indexTree(ctx));
917
- if (gated !== null)
918
- return gated;
919
799
  const repairPaths = await git(ctx, ["diff", "--cached", "--name-only", "-z"]);
920
800
  const repairReason = facts.failingCommandNames.length > 0
921
801
  ? `repair for failing check(s): ${facts.failingCommandNames.join(", ")}`
@@ -953,7 +833,7 @@ export function createReconcileOverlapDriver(job, payload, seams) {
953
833
  !(await isAncestor(ctx, payload.childHead, candidate)) ||
954
834
  !(await isAncestor(ctx, payload.requiredEpicSha, candidate))) {
955
835
  await resetToChildHead(ctx);
956
- return fail(ReconcileOverlapTopologyRefused, "governance-scanned", "the candidate is not a merge of the dispatched heads");
836
+ return fail(ReconcileOverlapTopologyRefused, "checked", "the candidate is not a merge of the dispatched heads");
957
837
  }
958
838
  return pushFlow(ctx);
959
839
  },
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Types for the `reconcile_overlap` executor job (BAPI-1152).
3
3
  *
4
- * The server authors the payload (`api/library/epic_conductor/reconcile_overlap_payload.py`)
4
+ * The server authors the payload (`api/library/epic_implementer/reconcile_overlap_payload.py`)
5
5
  * and verifies the result (`reconcile_overlap_completion_contract.py`); these
6
6
  * interfaces are the executor's view of both. They are deliberately plain data —
7
7
  * validation lives in `reconcile-overlap-job.ts`, which refuses anything that does
@@ -64,7 +64,7 @@ export function formatDispatcherUnavailableDiagnostic(liveness) {
64
64
  return (`executor: no fresh reconciler/dispatcher heartbeat is available ` +
65
65
  `(reconciler_liveness=${liveness}); the dispatcher may not be running. ` +
66
66
  "Check DISABLE_SCHEDULER and GET /automation/health; see " +
67
- "docs/claude/epic-conductor-v2-operator-runbook.md. Continuing to poll.");
67
+ "docs/claude/epic-implementer-v2-operator-runbook.md. Continuing to poll.");
68
68
  }
69
69
  /** The recovery diagnostic, emitted once when an absent dispatcher comes back. */
70
70
  export function formatDispatcherRecoveredDiagnostic() {
@@ -1323,7 +1323,7 @@ export async function runConductEpicInit(deps, options) {
1323
1323
  "Planned (dry run — nothing was pushed, cut, seeded, repointed, or written):",
1324
1324
  ...describePlan(),
1325
1325
  `would open a draft pull request ${epicBranch} → ${preflight.baseBranch} for ` +
1326
- "conductor-ci / gate (BAPI-951; nothing opened in dry-run)",
1326
+ "epic-implementer-ci / gate (BAPI-951; nothing opened in dry-run)",
1327
1327
  ]);
1328
1328
  }
1329
1329
  // The FIRST durable mutation of the whole verb, deliberately placed here: every