@bridge_gpt/mcp-server 0.2.49 → 0.2.50

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 (47) hide show
  1. package/README.md +24 -7
  2. package/build/base-ref.js +28 -3
  3. package/build/claude-review-workflow-drift-probe.js +130 -0
  4. package/build/claude-review-workflow-drift.js +173 -0
  5. package/build/claude-review-workflow.js +81 -16
  6. package/build/commands.generated.js +5 -5
  7. package/build/conductor/done-gate.js +25 -3
  8. package/build/conductor/install-doctor.js +65 -5
  9. package/build/conductor/latest-check-selector.js +170 -0
  10. package/build/conductor/local-merge.js +8 -6
  11. package/build/conductor-bin.js +1 -1
  12. package/build/{brainstorm-files.js → council-files.js} +15 -15
  13. package/build/decision-page-schema.js +1 -1
  14. package/build/docs.generated.js +1 -1
  15. package/build/doctor.js +162 -4
  16. package/build/executor/worktree.js +46 -1
  17. package/build/index.js +92 -51
  18. package/build/init.js +9 -2
  19. package/build/install-bridge.js +60 -2
  20. package/build/install-reexec.js +47 -9
  21. package/build/pipelines.generated.js +1 -1
  22. package/build/plane/cli.js +12 -2
  23. package/build/plane/manifest.js +25 -1
  24. package/build/plane/member-roster.js +61 -7
  25. package/build/plane/preflight.js +24 -9
  26. package/build/plane/supervisor.js +77 -5
  27. package/build/plane/types.js +23 -3
  28. package/build/readme.generated.js +1 -1
  29. package/build/run-unit-tests-launcher.js +2 -1
  30. package/build/stale-worktree-doctor.js +120 -0
  31. package/build/start-tickets-prereqs.js +70 -0
  32. package/build/start-tickets.js +91 -3
  33. package/build/version.generated.js +3 -2
  34. package/package.json +4 -2
  35. package/build/chain-orchestrator.js +0 -1457
  36. package/build/chain-utils.js +0 -68
  37. package/build/command-catalog.js +0 -376
  38. package/build/schedule-run.js +0 -1300
  39. package/build/schedule-store.js +0 -172
  40. package/build/scheduled-prompt.js +0 -115
  41. package/build/scheduler-backends/at-fallback.js +0 -139
  42. package/build/scheduler-backends/escaping.js +0 -143
  43. package/build/scheduler-backends/index.js +0 -72
  44. package/build/scheduler-backends/launchd.js +0 -225
  45. package/build/scheduler-backends/systemd-user.js +0 -250
  46. package/build/scheduler-backends/task-scheduler.js +0 -214
  47. package/build/scheduler-backends/types.js +0 -23
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Read-only stale-branch diagnostic (BAPI-948).
3
+ *
4
+ * `start-tickets --guard-stale-branch` (and `--conductor`, which implies it) refuses
5
+ * to reuse a pre-existing `feature/<KEY>` branch whose tip is not an ancestor of the
6
+ * resolved base. That refusal is correct but it only tells you AFTER you tried to cut
7
+ * the worktree. This module answers the same question BEFORE the cut, and without
8
+ * touching a single ref.
9
+ *
10
+ * It owns no ancestry logic of its own. `branchExists` and
11
+ * `isExistingBranchSafeToReuse` from `./worktree-core.js` are the exact predicates the
12
+ * guard itself runs, so a `would-refuse` finding here and a `create-failed` row there
13
+ * can never disagree — and the operator sentence is the same sentence in both places.
14
+ * Deliberately NOT a second ahead-count / diff / merge-base implementation: a
15
+ * diagnostic that re-derives the condition is a diagnostic that can be wrong about it.
16
+ *
17
+ * Strictly read-only. The only commands it can reach are the two the predicates issue
18
+ * — `git show-ref --verify --quiet`, `git rev-parse --verify --quiet`, and
19
+ * `git merge-base --is-ancestor` — none of which mutate anything. There is no fetch,
20
+ * reset, switch, checkout, branch deletion, or worktree creation on any path here, and
21
+ * nothing in this module writes a file.
22
+ */
23
+ import { branchExists, isExistingBranchSafeToReuse } from "./worktree-core.js";
24
+ /**
25
+ * The one sentence an `indeterminate` finding renders. Fixed text: the underlying
26
+ * throw is swallowed rather than formatted, so no probe failure can smuggle command
27
+ * output into a report.
28
+ */
29
+ export const STALE_WORKTREE_INDETERMINATE_REASON = "the read-only branch check could not complete (git was unavailable or failed to run); " +
30
+ "re-run the diagnostic, or inspect the branch by hand with " +
31
+ "`git merge-base --is-ancestor <branch> <base>`.";
32
+ /**
33
+ * Diagnose each target independently, read-only.
34
+ *
35
+ * Independence is the point: one unreadable branch must not blank out its siblings'
36
+ * findings, which is why every target is wrapped on its own rather than the loop as a
37
+ * whole. Targets are evaluated in the order given so the rendered report is stable.
38
+ */
39
+ export async function diagnoseStaleWorktreeBranches(deps, targets) {
40
+ const findings = [];
41
+ for (const target of targets) {
42
+ const { key, branch, baseStartPoint } = target;
43
+ try {
44
+ // No local branch → nothing pre-existing for the guard to act on. Return before
45
+ // the ancestry probe: running it would be a wasted command AND would produce a
46
+ // "not an ancestor" reason about a branch that is not there.
47
+ if (!(await branchExists(deps, branch))) {
48
+ findings.push({ key, branch, baseStartPoint, status: "branch-absent" });
49
+ continue;
50
+ }
51
+ // The SAME predicate `createWorktreeForTicket` runs, with the same branch and
52
+ // the same base start point, so the verdict and its remedy are identical to the
53
+ // refusal the guarded cut would emit.
54
+ const safety = await isExistingBranchSafeToReuse(deps, branch, baseStartPoint);
55
+ if (safety.safe) {
56
+ findings.push({ key, branch, baseStartPoint, status: "safe" });
57
+ continue;
58
+ }
59
+ findings.push({
60
+ key,
61
+ branch,
62
+ baseStartPoint,
63
+ status: "would-refuse",
64
+ reason: safety.reason,
65
+ });
66
+ }
67
+ catch {
68
+ findings.push({
69
+ key,
70
+ branch,
71
+ baseStartPoint,
72
+ status: "indeterminate",
73
+ reason: STALE_WORKTREE_INDETERMINATE_REASON,
74
+ });
75
+ }
76
+ }
77
+ return findings;
78
+ }
79
+ /**
80
+ * Render the advisory section. Advisory in the strict doctor sense: the caller never
81
+ * derives an exit code from it.
82
+ *
83
+ * One row per target, each naming the ticket, the resolved branch, and the base it was
84
+ * measured against — an operator diagnosing an epic cut needs all three to act. A
85
+ * `WOULD REFUSE` row is followed by the predicate's remedy on its own indented line
86
+ * rather than being folded into the row, because that sentence is the thing the
87
+ * operator has to read.
88
+ */
89
+ export function formatStaleWorktreeDiagnosticReport(findings) {
90
+ const lines = ["", "Stale ticket branches (advisory)"];
91
+ if (findings.length === 0) {
92
+ lines.push(" SKIP No --stale-branch targets supplied; nothing was checked.");
93
+ return lines.join("\n");
94
+ }
95
+ for (const finding of findings) {
96
+ const where = `${finding.key}: ${finding.branch} vs ${finding.baseStartPoint}`;
97
+ switch (finding.status) {
98
+ case "branch-absent":
99
+ lines.push(` OK ${where} — no local branch; a fresh one is cut from the base.`);
100
+ break;
101
+ case "safe":
102
+ lines.push(` OK ${where} — branch is an ancestor of the base; safe to reuse.`);
103
+ break;
104
+ case "would-refuse":
105
+ lines.push(` WARN ${where} — WOULD REFUSE.`);
106
+ if (finding.reason)
107
+ lines.push(` ${finding.reason}`);
108
+ break;
109
+ case "indeterminate":
110
+ lines.push(` WARN ${where} — indeterminate.`);
111
+ if (finding.reason)
112
+ lines.push(` ${finding.reason}`);
113
+ break;
114
+ }
115
+ }
116
+ lines.push(" Read-only: this section reads current local refs only. It never fetches,");
117
+ lines.push(" resets, switches, deletes, or repairs any git state, and never changes the");
118
+ lines.push(" exit code.");
119
+ return lines.join("\n");
120
+ }
@@ -19,6 +19,11 @@ import { resolveStartTicketsRepoName } from "./start-tickets-repo.js";
19
19
  import { probeWorktreeMcpRegistration } from "./mcp-registration-doctor.js";
20
20
  import { probeWorktreeCommandAssets } from "./command-assets-doctor.js";
21
21
  import { MCP_SERVER_NAME, MCP_PACKAGE_NAME } from "./mcp-identity.js";
22
+ // BAPI-941: both are LEAF modules (the pure classifier and its git-backed
23
+ // acquisition), so importing them here cannot close a cycle with `base-ref.ts`,
24
+ // which imports `commandSucceeded` from this module.
25
+ import { CLAUDE_REVIEW_WORKFLOW_DRIFT_REMEDIATION, CLAUDE_REVIEW_WORKFLOW_RELPATH, summarizeClaudeReviewWorkflowDrift, } from "./claude-review-workflow-drift.js";
26
+ import { probeClaudeReviewWorkflowDrift, resolveCurrentBranch, resolveRepositoryDefaultBranch, } from "./claude-review-workflow-drift-probe.js";
22
27
  import { CLAUDE_MCP_SHADOWING_REMEDIATION_COMMAND, claudeMcpShadowingRemediationCommand, formatClaudeMcpShadowFinding, formatClaudeUserConfigDiagnostic, inspectClaudeUserConfigForMcpShadowing, resolveClaudeUserConfigPath, } from "./claude-user-config-doctor.js";
23
28
  // ---------------------------------------------------------------------------
24
29
  // Constants (moved here from start-tickets.ts so both consumers share them)
@@ -717,6 +722,67 @@ export function liveSourceGuardDescriptor() {
717
722
  },
718
723
  };
719
724
  }
725
+ /** Secret-free remediation hint for the workflow-drift diagnostic. */
726
+ const CLAUDE_REVIEW_WORKFLOW_DRIFT_INSTALL_HINTS = {
727
+ darwin: CLAUDE_REVIEW_WORKFLOW_DRIFT_REMEDIATION,
728
+ linux: CLAUDE_REVIEW_WORKFLOW_DRIFT_REMEDIATION,
729
+ win32: CLAUDE_REVIEW_WORKFLOW_DRIFT_REMEDIATION,
730
+ };
731
+ /**
732
+ * BAPI-941 doctor-only, strictly read-only probe: does the current branch's copy
733
+ * of the reviewed workflow match the repository default branch's copy?
734
+ *
735
+ * Answers, without spawning a worker or creating a worktree, the question that
736
+ * otherwise only surfaces an hour into a conductor run: has this branch gone
737
+ * blind to automated code review? `anthropics/claude-code-action` refuses to run
738
+ * when the invoking workflow differs from the default branch's copy, and a
739
+ * long-lived `epic/**` branch inherits that staleness into every pull request
740
+ * based on it — while the pull requests themselves change no workflow file at
741
+ * all, so no path-based guard fires.
742
+ *
743
+ * **Read-only, and network-free.** It runs `git symbolic-ref`, `git rev-parse`,
744
+ * and two `git show` invocations against the local object database. It never
745
+ * fetches, checks out, installs, migrates, creates a worktree, or writes a
746
+ * credential.
747
+ *
748
+ * **Never blocking.** `doctor`'s exit code is
749
+ * `results.some((r) => !r.found) ? 1 : 0`, so this probe reports `found: true`
750
+ * for ALL THREE outcomes and carries the state in `detail`. Confirmed drift is an
751
+ * advisory an operator should act on, not a missing prerequisite that fails the
752
+ * command — and an unavailable comparison is uncertainty, which is even less of
753
+ * a fault. Registered in `getDoctorOnlyPrereqDescriptors`, never in
754
+ * `getPreflightPrereqDescriptors`.
755
+ *
756
+ * **Bounded output.** Every rendered string comes from the shared classifier's
757
+ * closed vocabulary plus refs and the workflow path. Subprocess stderr, GitHub
758
+ * exception text, and file content never reach the report.
759
+ */
760
+ export function claudeReviewWorkflowDriftDescriptor() {
761
+ return {
762
+ id: "claude-review-workflow-drift",
763
+ label: "claude-review workflow lineage",
764
+ installHint: CLAUDE_REVIEW_WORKFLOW_DRIFT_INSTALL_HINTS,
765
+ probe: async (deps) => {
766
+ const probeDeps = { runCommand: deps.runCommand, cwd: deps.cwd };
767
+ const baseRef = await resolveCurrentBranch(probeDeps);
768
+ if (!baseRef) {
769
+ // A detached HEAD has no branch whose lineage to report. Inconclusive,
770
+ // not a fault.
771
+ return {
772
+ found: true,
773
+ detail: `comparison unavailable (base_ref_unresolved) — advisory check failed open, ` +
774
+ `${CLAUDE_REVIEW_WORKFLOW_RELPATH} lineage unknown`,
775
+ };
776
+ }
777
+ const defaultRef = await resolveRepositoryDefaultBranch(probeDeps);
778
+ const classification = await probeClaudeReviewWorkflowDrift(probeDeps, {
779
+ baseRef,
780
+ defaultRef,
781
+ });
782
+ return { found: true, detail: summarizeClaudeReviewWorkflowDrift(classification) };
783
+ },
784
+ };
785
+ }
720
786
  /**
721
787
  * The exact existing live-preflight requirement set per platform, plus the git
722
788
  * work-tree custom check appended after the command checks. This is the ONLY
@@ -765,6 +831,10 @@ export function getDoctorOnlyPrereqDescriptors(_platform, _env, agent) {
765
831
  reviewTicketsGitDescriptor(),
766
832
  liveSourceGuardDescriptor(),
767
833
  claudeMcpShadowingDescriptor(),
834
+ // BAPI-941: advisory workflow-lineage diagnostic. Doctor-only by design —
835
+ // adding it to `getPreflightPrereqDescriptors` would make a stale base
836
+ // branch block worktree creation, which is the opposite of fail-open.
837
+ claudeReviewWorkflowDriftDescriptor(),
768
838
  ];
769
839
  }
770
840
  /**
@@ -60,6 +60,9 @@ import path from "path";
60
60
  import { VERSION } from "./version.generated.js";
61
61
  import { resolveBapiCredentials, getPrimaryCredentialStorePath, } from "./credential-store.js";
62
62
  import { MCP_PACKAGE_NAME } from "./mcp-identity.js";
63
+ // BAPI-941: the shared drift diagnostic formatter. The leaf drift module imports
64
+ // nothing from here, so this direction stays acyclic.
65
+ import { formatClaudeReviewWorkflowDriftDiagnostic } from "./claude-review-workflow-drift.js";
63
66
  import { resolveStartTicketsRepoName as resolveSharedStartTicketsRepoName, resolveRequiredStartTicketsRepoName, } from "./start-tickets-repo.js";
64
67
  import { provisionMcpRegistrationsForCreatedWorktrees, } from "./mcp-provisioning.js";
65
68
  import { provisionCommandsForCreatedWorktrees, } from "./command-provisioning.js";
@@ -132,7 +135,8 @@ export function getStartTicketsUsage() {
132
135
  " --base-branch BRANCH Cut new worktrees from BRANCH and refresh origin/BRANCH (default: main)",
133
136
  " --no-refresh-main Skip refresh of the configured base branch (default main); historical name retained for backward compatibility",
134
137
  " --max-parallel N Max worktrees to create concurrently (default: 3)",
135
- " --conductor Enable the Conductor system: per-worker hook injection + BAPI_CONDUCTOR_* env, lifecycle ledger events, and a supervisor peer tab (default: off — a plain run just spawns cd <worktree> && <agent> '/implement-ticket <KEY>')",
138
+ " --conductor Enable the Conductor system: per-worker hook injection + BAPI_CONDUCTOR_* env, lifecycle ledger events, and a supervisor peer tab (default: off — a plain run just spawns cd <worktree> && <agent> '/implement-ticket <KEY>'). Implies --guard-stale-branch: a pre-existing ticket branch carrying commits beyond the resolved base is refused, not silently reused.",
139
+ " --guard-stale-branch Refuse to reuse a pre-existing feature/KEY branch whose tip is NOT an ancestor of the resolved base (it carries commits the base does not). The ticket is reported create-failed with the stale-worktree remedy instead of spawning a worker on leftover code. Implied by --conductor; pass it explicitly on the LLM-conductor pilot cut path. Off by default, so a plain interactive run keeps reusing a same-named branch.",
136
140
  " -h, --help Show this help",
137
141
  "",
138
142
  "Environment:",
@@ -176,6 +180,11 @@ export function parseStartTicketsArgs(argv) {
176
180
  let agentName = DEFAULT_AGENT_NAME;
177
181
  let baseBranch = "main";
178
182
  let conductorEnabled = false;
183
+ // BAPI-948: `--guard-stale-branch` (and `--conductor`, which implies it) opt the
184
+ // packaged CLI into the F7 stale-branch guard. Tracked separately from
185
+ // `conductorEnabled` so the pilot can take the safety check WITHOUT taking the
186
+ // v2 conductor context that `--conductor` also switches on.
187
+ let guardStaleBranch = false;
179
188
  let workflow = "implement";
180
189
  let reviewRoundsRaw;
181
190
  // BAPI-642 `--tier` override. `undefined` = flag absent (property omitted from
@@ -358,6 +367,10 @@ export function parseStartTicketsArgs(argv) {
358
367
  conductorEnabled = true;
359
368
  continue;
360
369
  }
370
+ if (arg === "--guard-stale-branch") {
371
+ guardStaleBranch = true;
372
+ continue;
373
+ }
361
374
  if (arg === "--no-refresh-main") {
362
375
  refreshMain = false;
363
376
  continue;
@@ -454,6 +467,11 @@ export function parseStartTicketsArgs(argv) {
454
467
  conductorEnabled,
455
468
  workflow,
456
469
  reviewRounds,
470
+ // BAPI-948: include `guardStaleWorktree` ONLY when the run asked for the F7
471
+ // guard — `--guard-stale-branch`, or `--conductor` which implies it. A plain
472
+ // interactive invocation leaves the property entirely absent (not `false`),
473
+ // so the reuse-friendly human option shape is byte-identical to before.
474
+ ...(guardStaleBranch || conductorEnabled ? { guardStaleWorktree: true } : {}),
457
475
  // BAPI-642: include the injected-tier property ONLY when `--tier` was
458
476
  // supplied, so an omitted flag leaves the legacy option shape untouched
459
477
  // (no own `injectedTier` key, not even `undefined`).
@@ -834,9 +852,23 @@ export async function refreshBaseBranch(deps, options) {
834
852
  return { ok: true };
835
853
  const baseBranch = options.baseBranch;
836
854
  const originRef = `origin/${baseBranch}`;
837
- const fetch = await deps.runCommand("git", ["fetch", "origin", baseBranch], {
855
+ // BAPI-941: refresh the DEFAULT branch alongside the base in ONE invocation so
856
+ // the caller's workflow-drift advisory reads an up-to-date `origin/<default>`
857
+ // without a second network round trip. Deduplicated when they are the same
858
+ // branch, and retried without the extra refspec on failure so this enrichment
859
+ // can never convert a working refresh into an error.
860
+ const defaultBranch = await resolveRepositoryDefaultBranch(deps);
861
+ const extraRefs = defaultBranch && defaultBranch !== baseBranch && validateBranchName(defaultBranch) === null
862
+ ? [defaultBranch]
863
+ : [];
864
+ let fetch = await deps.runCommand("git", ["fetch", "origin", baseBranch, ...extraRefs], {
838
865
  cwd: deps.cwd,
839
866
  });
867
+ if (!commandSucceeded(fetch) && extraRefs.length > 0) {
868
+ fetch = await deps.runCommand("git", ["fetch", "origin", baseBranch], {
869
+ cwd: deps.cwd,
870
+ });
871
+ }
840
872
  if (!commandSucceeded(fetch)) {
841
873
  return {
842
874
  ok: false,
@@ -934,12 +966,49 @@ export async function runWithConcurrency(items, limit, worker) {
934
966
  // same function references.
935
967
  import { resolveBranchForTicket, branchExists, buildWtSwitchArgs, pathApiForPlatform, extractWorktreePath, isExistingBranchSafeToReuse, createWorktreeForTicket, } from "./worktree-core.js";
936
968
  export { resolveBranchForTicket, branchExists, buildWtSwitchArgs, pathApiForPlatform, extractWorktreePath, isExistingBranchSafeToReuse, createWorktreeForTicket, };
969
+ /**
970
+ * BAPI-941: emit the per-spawn `claude-review` workflow-drift advisory.
971
+ *
972
+ * Called once at the shared post-base-resolution, pre-worktree choke point. It
973
+ * returns `void` and swallows everything: this diagnostic exists to tell an
974
+ * operator that their epic branch has gone blind to code review, and a
975
+ * diagnostic that can abort the run it is describing would be strictly worse
976
+ * than no diagnostic at all.
977
+ *
978
+ * All three outcomes continue to worktree creation:
979
+ *
980
+ * - `drifted` — a WARNING naming the stale base and the remedy, and saying
981
+ * plainly that work continues.
982
+ * - `unverified` — an INFO line naming which comparison was unavailable
983
+ * (missing ref, unreadable workflow, unresolved default branch).
984
+ * - `aligned` — silence. A clean spawn prints nothing and performs no
985
+ * additional network work beyond the fetch that already happened.
986
+ */
987
+ async function emitClaudeReviewWorkflowDriftAdvisory(deps, baseBranch, overrides) {
988
+ const warn = overrides.workflowDriftWarningLog ?? ((m) => console.error(m));
989
+ try {
990
+ const probe = overrides.probeWorkflowDrift ?? probeClaudeReviewWorkflowDrift;
991
+ const defaultRef = await resolveRepositoryDefaultBranch(deps);
992
+ const classification = await probe(deps, { baseRef: baseBranch, defaultRef });
993
+ if (classification.state === "aligned")
994
+ return;
995
+ warn(formatClaudeReviewWorkflowDriftDiagnostic(classification));
996
+ }
997
+ catch {
998
+ // A thrown probe is itself an unavailable comparison. Stay silent about the
999
+ // exception (it can echo absolute paths and provider text) and never let it
1000
+ // reach the caller.
1001
+ }
1002
+ }
937
1003
  // BAPI-586: base-ref resolution now lives in the focused `base-ref.ts` module so
938
1004
  // the executor can depend on it without pulling in this large CLI module. We
939
1005
  // import the VALUES here (so internal uses keep working) and re-export the same
940
1006
  // references so existing importers of `./start-tickets.js` (index.ts,
941
1007
  // review-tickets.ts) and the pinned start-tickets tests are unaffected.
942
- import { validateBranchName, fetchAndResolveBaseSha } from "./base-ref.js";
1008
+ import { validateBranchName, fetchAndResolveBaseSha,
1009
+ // BAPI-941: both live in the same leaf module so the interactive refresh path
1010
+ // and the non-mutating conductor path share one drift implementation.
1011
+ resolveRepositoryDefaultBranch, probeClaudeReviewWorkflowDrift, } from "./base-ref.js";
943
1012
  export { validateBranchName, fetchAndResolveBaseSha };
944
1013
  /**
945
1014
  * Create / switch worktrees for every ticket, throttled to `maxParallel`, using
@@ -2898,6 +2967,22 @@ export async function orchestrateStartTickets(deps, options, overrides = {}) {
2898
2967
  if (!refresh.ok)
2899
2968
  return { ok: false, error: refresh.error };
2900
2969
  }
2970
+ // BAPI-941: the `claude-review` workflow-drift advisory, emitted ONCE per
2971
+ // worker spawn at the one choke point both dispatch paths pass through, after
2972
+ // the base is resolved and before any worktree exists.
2973
+ //
2974
+ // Per-spawn timing is the whole point. `epic/BAPI-902` was cut clean and was
2975
+ // blinded 24 hours later by an unrelated commit on `main`, so a check that ran
2976
+ // only at `init`/`setup-epic` time would have passed and still let every
2977
+ // subsequent PR go unreviewed. Both refs were just refreshed by the fetch
2978
+ // above, so the comparison is local and costs no extra round trip.
2979
+ //
2980
+ // ADVISORY ONLY, and fail-open in every degraded case (unreachable remote,
2981
+ // missing ref, unresolvable default branch): it prints to stderr and the run
2982
+ // continues to worktree creation. It is not a `RunPolicy` key and not a
2983
+ // worker-emitted control signal — R14 rules 2 and 3 are about the conductor's
2984
+ // decision surface, and a spawner's warning is neither.
2985
+ await emitClaudeReviewWorkflowDriftAdvisory(deps, options.baseBranch, overrides);
2901
2986
  const createWorktreesFn = overrides.createWorktrees ?? createWorktrees;
2902
2987
  const provisionCommandsFn = overrides.provisionCommands ??
2903
2988
  ((rows, d) => provisionCommandsForCreatedWorktrees(rows, buildCommandProvisioningDeps(d)));
@@ -3113,6 +3198,9 @@ export async function runStartTicketsCli(argv, overrides = {}) {
3113
3198
  modelRoutingLog: log,
3114
3199
  modelRoutingWarningLog: errorLog,
3115
3200
  repoNameWarningLog: errorLog,
3201
+ // BAPI-941: the workflow-drift advisory goes to stderr like every other
3202
+ // spawner diagnostic, so it never contaminates the summary on stdout.
3203
+ workflowDriftWarningLog: errorLog,
3116
3204
  // Conductor is OPT-IN (BAPI-394): the three seams are supplied ONLY when the
3117
3205
  // user passes `--conductor`. Supplying `createConductorContext` activates the
3118
3206
  // conductor stage inside orchestrate (env injection + supervisor tab); the
@@ -1,3 +1,4 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
- export const VERSION = "0.2.49";
3
- export const BUILD_COMMIT = "ccff42dd5212";
2
+ export const VERSION = "0.2.50";
3
+ export const BUILD_COMMIT = "4b5d3e65b2a3";
4
+ export const LAUNCHER_ARGS = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.50", "serve"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.2.49",
3
+ "version": "0.2.50",
4
4
  "description": "Bridge API MCP server — exposes Jira endpoints as MCP tools for Claude Code agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -21,8 +21,10 @@
21
21
  "LICENSE"
22
22
  ],
23
23
  "//test": "The `test` script delegates to the bounded launcher (scripts/run-unit-tests.js → src/run-unit-tests-launcher.ts, BAPI-683), which discovers build/*.test.js at runtime and spawns them in size-bounded batches so the argv never exceeds Windows' ~8191-char command-line limit. Two determinism rules from the pre-launcher script are preserved INSIDE the launcher: (1) multi-file batches run WITHOUT --test-force-exit, because that flag makes node:test exit before aggregating per-subprocess summaries and the tally becomes nondeterministic (failures still surface and still set exit 1, but the count cannot be trusted as a completeness signal); (2) build/secret-safety.test.js leaks a handle that holds the event loop ~60s after its tests finish (they run in ~147ms), so the launcher quarantines it into its own single-file --test-force-exit invocation (FORCE_EXIT_QUARANTINE in src/run-unit-tests-launcher.ts) where the tally stays exact. Do NOT add --test-force-exit to batched invocations, and do NOT remove the quarantine, until the underlying handle leak is fixed.",
24
+ "//version": "BAPI-930: fired by `npm version <level>` AFTER npm rewrites package.json's version field, so the bumped version and the repinned README land in the SAME commit. A bump performed by hand-editing package.json instead of running `npm version` bypasses this hook entirely and must repin manually via `node scripts/sync-readme-host-examples.js` — the `--check` wired into `build` below is the backstop that catches that case.",
24
25
  "scripts": {
25
- "build": "node scripts/bundle-version.js && node scripts/validate-packaged-command-references.js && node scripts/bundle-readme.js && node scripts/bundle-pipelines.js && node scripts/bundle-commands.js && node scripts/bundle-agents.js && node scripts/bundle-docs.js && tsc && node scripts/sync-agent-mirrors.js && node scripts/bundle-esbuild.js",
26
+ "build": "node scripts/bundle-version.js && node scripts/sync-readme-host-examples.js --check && node scripts/validate-packaged-command-references.js && node scripts/bundle-readme.js && node scripts/bundle-pipelines.js && node scripts/bundle-commands.js && node scripts/bundle-agents.js && node scripts/bundle-docs.js && tsc && node scripts/sync-agent-mirrors.js && node scripts/bundle-esbuild.js",
27
+ "version": "node scripts/sync-readme-host-examples.js --write && git add -- README.md package.json package-lock.json && git commit -m \"chore(mcp-server): release $npm_package_version\" && git tag \"mcp-server/v$npm_package_version\"",
26
28
  "check:version-generated": "node scripts/bundle-version.js && node scripts/check-version-generated.js",
27
29
  "postbuild": "node scripts/prepend-shebang.cjs",
28
30
  "start": "node build/index.js",