@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
@@ -197,6 +197,355 @@ export function presentRequiredChecks(supervisor) {
197
197
  };
198
198
  }
199
199
  // ---------------------------------------------------------------------------
200
+ // Gate-profile wording (BAPI-1390) — shared with the readiness gate's rows
201
+ // ---------------------------------------------------------------------------
202
+ /**
203
+ * Render a server-validated check or command name for a remedy. The name is
204
+ * the ONLY payload value any remedy interpolates; backticks are swapped so it
205
+ * cannot close its own quoting.
206
+ */
207
+ export function quotedSubject(subject, fallback) {
208
+ if (subject === null)
209
+ return fallback;
210
+ const safe = subject.replace(/`/g, "'");
211
+ return `\`${safe}\``;
212
+ }
213
+ /** Human words for the profile kind. `null` is an unknown kind, never a held one. */
214
+ const GATE_PROFILE_KIND_LABELS = {
215
+ provider: "Provider",
216
+ local: "Local",
217
+ mixed: "Mixed",
218
+ none: "None",
219
+ };
220
+ /**
221
+ * One line describing a gate profile: kind, source, confirmation, revision,
222
+ * members, migration guard, and qualification state. Shared with the install
223
+ * doctor's section so the two surfaces cannot word the same profile apart.
224
+ * Only validated names are interpolated; everything else is a closed token.
225
+ */
226
+ export function describeGateProfile(profile) {
227
+ const kind = profile.kind === null ? "Unknown" : GATE_PROFILE_KIND_LABELS[profile.kind] ?? "Unknown";
228
+ const source = profile.source === "legacy"
229
+ ? "legacy: pre-profile done-gate checks treated as a confirmed provider profile"
230
+ : profile.source;
231
+ const parts = [
232
+ `${kind} gate profile (${source}, ${profile.confirmed ? "confirmed" : "unconfirmed"}, revision ${profile.revision})`,
233
+ ];
234
+ if (profile.provider_checks.length > 0) {
235
+ parts.push(`provider checks: ${profile.provider_checks
236
+ .map((c) => `${quotedSubject(c.name, "")} ${c.outcome === "evidence_unavailable" ? "unavailable" : c.outcome ?? "unknown"}`)
237
+ .join(", ")}`);
238
+ }
239
+ if (profile.local_commands.length > 0) {
240
+ parts.push(`local commands: ${profile.local_commands.map((c) => quotedSubject(c.name, "")).join(", ")}`);
241
+ }
242
+ parts.push(profile.migration_guard === null
243
+ ? profile.migration_guard_required === true
244
+ ? "migration guard: unset (required)"
245
+ : "migration guard: none"
246
+ : `migration guard: ${profile.migration_guard.kind}${profile.migration_guard.name === null ? "" : ` ${quotedSubject(profile.migration_guard.name, "")}`}`);
247
+ if (profile.qualification_state !== null)
248
+ parts.push(`qualification: ${profile.qualification_state}`);
249
+ if (profile.kind === "none") {
250
+ parts.push("unattended runs proceed, but every child merge waits for approval (`merge-child`)");
251
+ }
252
+ return parts.join("; ");
253
+ }
254
+ /** Fixed copy for a known warning code. Only the validated name is interpolated. */
255
+ export function describeGateProfileWarning(code, name) {
256
+ switch (code) {
257
+ case "provider_check_not_seen_on_epic_branches":
258
+ return `the check ${quotedSubject(name, "named in the gate profile")} has not been seen on an epic branch yet — this does not hold a launch`;
259
+ case "epic_branch_search_unavailable":
260
+ return "Bridge could not search epic branches for the gate-profile checks — this does not hold a launch";
261
+ default:
262
+ return "the gate profile reported a warning this CLI does not recognize — this does not hold a launch";
263
+ }
264
+ }
265
+ /** Section id for each known warn-only gate-profile code; anything else is generic. */
266
+ const GATE_PROFILE_WARNING_SECTION_IDS = {
267
+ provider_check_not_seen_on_epic_branches: "supervisor-gate-profile-epic-branch-unseen",
268
+ epic_branch_search_unavailable: "supervisor-gate-profile-epic-branch-search",
269
+ };
270
+ /**
271
+ * BAPI-1390 — the gate-profile section plus its warn-only rows. Emitted ONLY
272
+ * when the server reported a profile, so an older server's report is
273
+ * byte-identical. Never `fatal`: the server's admission evidence decides holds;
274
+ * these sections describe. Warnings are `degraded` with copy that says they do
275
+ * not hold a launch, one section per known code (names joined) plus one
276
+ * generic section for codes this client does not recognize.
277
+ */
278
+ export function gateProfileSections(profile) {
279
+ if (profile === undefined)
280
+ return [];
281
+ if (profile === null) {
282
+ return [
283
+ {
284
+ id: "supervisor-gate-profile",
285
+ label: "Supervisor gate profile",
286
+ status: "degraded",
287
+ detail: "the server reported no gate profile, so the CI gate is unverified",
288
+ remediation: "upgrade the Bridge API deploy so it reports the gate profile.",
289
+ },
290
+ ];
291
+ }
292
+ const detail = describeGateProfile(profile);
293
+ const held = profile.read_failed ||
294
+ profile.stored_malformed ||
295
+ profile.verdict === "denied" ||
296
+ profile.verdict === "unable_to_verify";
297
+ const sections = [];
298
+ if (held) {
299
+ sections.push({
300
+ id: "supervisor-gate-profile",
301
+ label: "Supervisor gate profile",
302
+ status: "degraded",
303
+ detail: profile.read_failed
304
+ ? `the stored gate profile could not be read — ${detail}`
305
+ : profile.stored_malformed
306
+ ? `the stored gate profile is malformed — ${detail}`
307
+ : `${profile.verdict === "denied" ? "denied" : "could not be verified"}${profile.reason ? ` (${profile.reason})` : ""} — ${detail}`,
308
+ remediation: "a repository administrator reviews the gate profile on the repository's Setup page; " +
309
+ "the server's launch evidence names the specific item.",
310
+ });
311
+ }
312
+ else if (profile.verdict === "confirmed" && profile.kind !== "none") {
313
+ sections.push({ id: "supervisor-gate-profile", label: "Supervisor gate profile", status: "ok", detail });
314
+ }
315
+ else {
316
+ sections.push({
317
+ id: "supervisor-gate-profile",
318
+ label: "Supervisor gate profile",
319
+ status: "degraded",
320
+ detail,
321
+ remediation: profile.kind === "none"
322
+ ? "no action is needed to launch; to let child merges proceed without approval, add checks to the gate profile."
323
+ : "a repository administrator confirms the gate profile; the server's launch evidence decides whether a run is held.",
324
+ });
325
+ }
326
+ const grouped = new Map();
327
+ for (const warning of profile.warnings) {
328
+ const id = GATE_PROFILE_WARNING_SECTION_IDS[warning.code] ?? "supervisor-gate-profile-warning";
329
+ const entry = grouped.get(id) ?? { code: warning.code, names: [] };
330
+ entry.names.push(warning.name);
331
+ grouped.set(id, entry);
332
+ }
333
+ for (const [id, { code, names }] of grouped) {
334
+ const named = names.filter((name) => name !== null);
335
+ sections.push({
336
+ id,
337
+ label: "Supervisor gate profile warning",
338
+ status: "degraded",
339
+ detail: code === "provider_check_not_seen_on_epic_branches" && named.length > 1
340
+ ? `the checks ${named.map((name) => quotedSubject(name, "")).join(", ")} have not been seen on an epic branch yet — this does not hold a launch`
341
+ : describeGateProfileWarning(code, named[0] ?? null),
342
+ remediation: "no action is needed to launch; this is a warning only.",
343
+ });
344
+ }
345
+ return sections;
346
+ }
347
+ // ---------------------------------------------------------------------------
348
+ // BAPI-1405 — epic-branch CI rows (warn-only)
349
+ // ---------------------------------------------------------------------------
350
+ /** Stable section ids for the three epic-branch CI rows. */
351
+ export const EPIC_BRANCH_CI_SECTION_IDS = {
352
+ reviewMode: "effective-review-mode",
353
+ decision: "epic-branch-ci-decision",
354
+ coverage: "epic-branch-ci-coverage-warning",
355
+ };
356
+ /** Row labels, shared by readiness and the install doctor. */
357
+ export const EPIC_BRANCH_CI_SECTION_LABELS = {
358
+ reviewMode: "Effective review mode",
359
+ decision: "Epic-branch CI decision",
360
+ coverage: "Epic-branch CI coverage warning",
361
+ };
362
+ /** What every row says when the server did not report the block. Never "no warning". */
363
+ export const EPIC_BRANCH_CI_UNREPORTED_DETAIL = "Unreported — upgrade the server";
364
+ /** The one remediation an unreported block carries. */
365
+ export const EPIC_BRANCH_CI_UNREPORTED_REMEDIATION = "upgrade the Bridge API deploy so it reports epic-branch CI facts; this does not hold a launch.";
366
+ /** The single next step for evidence that may be out of date or uncertain. */
367
+ export const EPIC_BRANCH_CI_RERUN_INSTALL_ADVICE = "Rerun install after workflow or ruleset changes.";
368
+ /** Plain words for each effective review mode. */
369
+ const EFFECTIVE_REVIEW_MODE_WORDS = {
370
+ repository_policy: "repository_policy — no review verdict is required for epic-branch merges",
371
+ verdict_protocol: "verdict_protocol — child merges wait for the claude-review verdict",
372
+ native_review_decision: "native_review_decision — child merges wait for GitHub's own review decision",
373
+ none: "none — no review signal gates child merges",
374
+ };
375
+ /** Plain words for each known no-CI reason. An unknown reason renders generically. */
376
+ function describeNoCiReason(reason) {
377
+ switch (reason) {
378
+ case "gate_profile_none":
379
+ return "the gate profile is `none`, so no CI check gates child merges";
380
+ case "gate_profile_local_only":
381
+ return "the gate is local-only, so on a feature-branch run no provider check runs on epic-branch PRs";
382
+ case "triggers_do_not_cover_epic":
383
+ return "the evidence recorded at install says no pull_request trigger covers epic/**";
384
+ default:
385
+ return `the server reported a reason this CLI does not recognize (${reason})`;
386
+ }
387
+ }
388
+ /** One next step per known no-CI reason. */
389
+ function noCiReasonNextStep(reason) {
390
+ switch (reason) {
391
+ case "gate_profile_none":
392
+ return "add provider checks to the gate profile";
393
+ case "gate_profile_local_only":
394
+ return "add a provider check to the gate profile if epic-branch PRs should run CI";
395
+ case "triggers_do_not_cover_epic":
396
+ return "add epic/** to your CI workflow's pull_request branches filter, then rerun install";
397
+ default:
398
+ return "upgrade this CLI to read the server's reason";
399
+ }
400
+ }
401
+ function yesNoUnknown(value) {
402
+ return value === null ? "could not tell" : value ? "yes" : "no";
403
+ }
404
+ /**
405
+ * The compact "recorded at install" evidence summary. Trigger coverage is
406
+ * worded as coverage, never as proof that CI runs.
407
+ */
408
+ export function describeEpicBranchCiEvidence(evidence) {
409
+ return ("recorded at install: " +
410
+ `pull_request trigger covers epic/** ${yesNoUnknown(evidence.workflow_triggers_cover_epic)}; ` +
411
+ `approvals apply on epic/** ${yesNoUnknown(evidence.required_approvals_apply_to_epic)}; ` +
412
+ `observation quality ${evidence.observation_quality === "could_not_tell" ? "could not tell" : evidence.observation_quality}; ` +
413
+ `exemption outcome ${evidence.exemption_outcome === "not_exempt" ? "not exempt" : evidence.exemption_outcome}`);
414
+ }
415
+ /**
416
+ * One reason per "could not tell" in the evidence, each in plain words. Empty
417
+ * when nothing is uncertain. The shared next step is
418
+ * {@link EPIC_BRANCH_CI_RERUN_INSTALL_ADVICE}.
419
+ */
420
+ export function epicBranchCiEvidenceUnknowns(evidence) {
421
+ const unknowns = [];
422
+ if (evidence.workflow_triggers_cover_epic === null) {
423
+ unknowns.push("trigger coverage could not tell: a workflow was not read confidently");
424
+ }
425
+ if (evidence.required_approvals_apply_to_epic === null) {
426
+ unknowns.push("approvals could not tell: the epic-branch rules were not readable");
427
+ }
428
+ if (evidence.observation_quality === "could_not_tell" &&
429
+ evidence.workflow_triggers_cover_epic !== null &&
430
+ evidence.required_approvals_apply_to_epic !== null) {
431
+ unknowns.push("observation quality could not tell: a finding was not confirmed");
432
+ }
433
+ if (evidence.exemption_outcome === "unknown") {
434
+ unknowns.push("exemption could not tell: the approval exemption was not confirmed");
435
+ }
436
+ return unknowns;
437
+ }
438
+ function effectiveReviewModeSection(block) {
439
+ const mode = block.effective_review_mode;
440
+ if (mode === null) {
441
+ return {
442
+ id: EPIC_BRANCH_CI_SECTION_IDS.reviewMode,
443
+ label: EPIC_BRANCH_CI_SECTION_LABELS.reviewMode,
444
+ status: "degraded",
445
+ detail: "the server reported a review mode this CLI does not recognize",
446
+ remediation: "upgrade this CLI so it can read the review mode; this does not hold a launch.",
447
+ };
448
+ }
449
+ return {
450
+ id: EPIC_BRANCH_CI_SECTION_IDS.reviewMode,
451
+ label: EPIC_BRANCH_CI_SECTION_LABELS.reviewMode,
452
+ status: "ok",
453
+ detail: EFFECTIVE_REVIEW_MODE_WORDS[mode],
454
+ };
455
+ }
456
+ function epicBranchCiDecisionSection(block) {
457
+ const choice = block.decision === "include"
458
+ ? "Include — child merges wait for the epic-branch CI checks"
459
+ : block.decision === "decline"
460
+ ? "Decline — child merges do not wait for CI checks on the epic branch; problems surface at the integration PR"
461
+ : "Not recorded — behaves like include";
462
+ const evidence = block.evidence;
463
+ const detail = evidence === null
464
+ ? `${choice}; no evidence was recorded at install`
465
+ : `${choice}; ${describeEpicBranchCiEvidence(evidence)}`;
466
+ const unknowns = evidence === null ? [] : epicBranchCiEvidenceUnknowns(evidence);
467
+ if (unknowns.length > 0) {
468
+ return {
469
+ id: EPIC_BRANCH_CI_SECTION_IDS.decision,
470
+ label: EPIC_BRANCH_CI_SECTION_LABELS.decision,
471
+ status: "degraded",
472
+ detail,
473
+ remediation: `${unknowns.join("; ")}. ${EPIC_BRANCH_CI_RERUN_INSTALL_ADVICE} This does not hold a launch.`,
474
+ };
475
+ }
476
+ return {
477
+ id: EPIC_BRANCH_CI_SECTION_IDS.decision,
478
+ label: EPIC_BRANCH_CI_SECTION_LABELS.decision,
479
+ status: "ok",
480
+ detail: block.decision === null
481
+ ? `${detail}; run install to record a decision`
482
+ : evidence === null
483
+ ? detail
484
+ : `${detail}. ${EPIC_BRANCH_CI_RERUN_INSTALL_ADVICE}`,
485
+ };
486
+ }
487
+ function epicBranchCiCoverageSection(block) {
488
+ const reasons = block.no_ci_reasons;
489
+ if (!block.no_ci_warning && reasons.length === 0) {
490
+ const triggers = block.evidence?.workflow_triggers_cover_epic ?? null;
491
+ return {
492
+ id: EPIC_BRANCH_CI_SECTION_IDS.coverage,
493
+ label: EPIC_BRANCH_CI_SECTION_LABELS.coverage,
494
+ status: "ok",
495
+ detail: triggers === null
496
+ ? "no warning from the gate profile; trigger coverage of epic/** was not recorded or could not be told at install"
497
+ : "no warning: the gate profile and the evidence recorded at install give no reason to expect epic-branch PRs to skip CI (trigger coverage does not guarantee CI runs)",
498
+ };
499
+ }
500
+ const described = reasons.map(describeNoCiReason);
501
+ return {
502
+ id: EPIC_BRANCH_CI_SECTION_IDS.coverage,
503
+ label: EPIC_BRANCH_CI_SECTION_LABELS.coverage,
504
+ status: "degraded",
505
+ detail: described.length === 0
506
+ ? "Warning: epic-branch PRs may run no CI (the server gave no reason)"
507
+ : `Warning: epic-branch PRs may run no CI — ${described.join("; ")}`,
508
+ remediation: (reasons.length === 0
509
+ ? "review the gate profile and your CI workflow triggers"
510
+ : reasons.map(noCiReasonNextStep).join("; ")) + ". This warning does not hold a launch.",
511
+ };
512
+ }
513
+ /**
514
+ * BAPI-1405 — the three warn-only epic-branch CI rows: **Effective review
515
+ * mode**, **Epic-branch CI decision**, and **Epic-branch CI coverage warning**.
516
+ *
517
+ * Always three rows, so an older server is visible: an absent or `null` block
518
+ * renders every row as {@link EPIC_BRANCH_CI_UNREPORTED_DETAIL}, never as "no
519
+ * warning". Never `fatal`: none of these rows may change an admission verdict,
520
+ * a readiness verdict, or an exit status. Status is carried by words in the
521
+ * detail as well as by the status field, never by color alone.
522
+ */
523
+ export function epicBranchCiSections(block) {
524
+ if (block === undefined || block === null) {
525
+ return ["reviewMode", "decision", "coverage"].map((key) => ({
526
+ id: EPIC_BRANCH_CI_SECTION_IDS[key],
527
+ label: EPIC_BRANCH_CI_SECTION_LABELS[key],
528
+ status: "degraded",
529
+ detail: EPIC_BRANCH_CI_UNREPORTED_DETAIL,
530
+ remediation: EPIC_BRANCH_CI_UNREPORTED_REMEDIATION,
531
+ }));
532
+ }
533
+ return [
534
+ effectiveReviewModeSection(block),
535
+ epicBranchCiDecisionSection(block),
536
+ epicBranchCiCoverageSection(block),
537
+ ];
538
+ }
539
+ /**
540
+ * True when the server says the effective review mode is `repository_policy`,
541
+ * which requires no review verdict, so the claude-review workflow and the
542
+ * review-policy alignment rows have nothing to nag about. Absent block → false
543
+ * (older behavior is kept exactly).
544
+ */
545
+ export function effectiveReviewModeIsRepositoryPolicy(readiness) {
546
+ return readiness?.epic_branch_ci?.effective_review_mode === "repository_policy";
547
+ }
548
+ // ---------------------------------------------------------------------------
200
549
  // Section builders
201
550
  // ---------------------------------------------------------------------------
202
551
  export function supervisorSection(readiness) {
@@ -253,7 +602,18 @@ export function supervisorSection(readiness) {
253
602
  // manufacture a finding out of a server that never answered.
254
603
  const alignment = s.review_policy_alignment;
255
604
  if (alignment !== null) {
256
- if (alignment.status === "divergent" || alignment.status === "invalid") {
605
+ if (alignment.status === "divergent" && effectiveReviewModeIsRepositoryPolicy(readiness)) {
606
+ // BAPI-1405 — `repository_policy` requires no review verdict, so a
607
+ // divergent done-gate review signal is not a nag here. `invalid` still
608
+ // warns: an unreadable review configuration is its own problem.
609
+ sections.push({
610
+ id: "supervisor-review-alignment",
611
+ label: "Supervisor review-policy alignment",
612
+ status: "ok",
613
+ detail: "not applicable: the effective review mode is 'repository_policy', which requires no review verdict",
614
+ });
615
+ }
616
+ else if (alignment.status === "divergent" || alignment.status === "invalid") {
257
617
  sections.push({
258
618
  id: "supervisor-review-alignment",
259
619
  label: "Supervisor review-policy alignment",
@@ -279,6 +639,8 @@ export function supervisorSection(readiness) {
279
639
  });
280
640
  }
281
641
  }
642
+ // BAPI-1390 — nothing at all when the server did not report a profile.
643
+ sections.push(...gateProfileSections(readiness.gate_profile));
282
644
  return sections;
283
645
  }
284
646
  export function githubCredentialsSection(readiness) {
@@ -526,7 +888,9 @@ export function workflowSection(presence, reviewPolicySource, driftDetail) {
526
888
  // never `absent`, and the policy-applicability branch below does not apply
527
889
  // (a stale workflow only matters to a policy that consumes its verdict, and
528
890
  // the caller only classifies drift when it does).
529
- if (presence === "drifted") {
891
+ // BAPI-1405: under `repository_policy` no verdict is consumed, so a drifted
892
+ // file falls through to the not-applicable branch below instead of nagging.
893
+ if (presence === "drifted" && reviewPolicySource !== "repository_policy") {
530
894
  return {
531
895
  id: "claude-review-workflow",
532
896
  label: "claude-review workflow",
@@ -540,7 +904,9 @@ export function workflowSection(presence, reviewPolicySource, driftDetail) {
540
904
  // review decision does not need the Claude workflow at all, so its absence is
541
905
  // not a gap. Reporting it as degraded there would train operators to ignore
542
906
  // this section.
543
- if (reviewPolicySource === "native_review_decision" || reviewPolicySource === "none") {
907
+ if (reviewPolicySource === "native_review_decision" ||
908
+ reviewPolicySource === "none" ||
909
+ reviewPolicySource === "repository_policy") {
544
910
  return {
545
911
  id: "claude-review-workflow",
546
912
  label: "claude-review workflow",