@bridge_gpt/mcp-server 0.2.23 → 0.2.25

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.
@@ -1842,6 +1842,10 @@ const GITHUB_CONNECT_MESSAGES = {
1842
1842
  'already-consumed': 'This GitHub connection link has already been used. Please start the connection again.',
1843
1843
  'unresolved': 'GitHub returned without completing the connection. Please try again.',
1844
1844
  'no-repositories': 'The GitHub App installation did not include any repositories Bridge can access.',
1845
+ // Honest about the dead end (BAPI-631): GitHub sends the org owner an approval
1846
+ // request out-of-band, and their approval does not redirect back here. Waiting on
1847
+ // this page accomplishes nothing, so say so and point at the fallback that works.
1848
+ 'awaiting-organization-approval': 'Your request to install the GitHub App was sent to an organization owner for approval. Approval happens on GitHub and will not return you to this page, so waiting here will not finish the connection. Once an owner approves the install, use the manual fallback below to finish connecting.',
1845
1849
  'already-linked': 'This GitHub installation is already connected to a different Bridge account. If this is unexpected, contact support.',
1846
1850
  'binding-failed': 'GitHub was verified, but Bridge could not finish linking the repository. Use the manual fallback below.',
1847
1851
  pickerLoading: 'Loading the repositories from your GitHub installation…',
@@ -1850,10 +1854,33 @@ const GITHUB_CONNECT_MESSAGES = {
1850
1854
  connectedTitle: 'GitHub connected',
1851
1855
  };
1852
1856
 
1857
+ // User-facing copy for the optional editor/MCP section and the install-key
1858
+ // outcomes (BAPI-630). GitHub-specific feedback stays in the sanitized
1859
+ // GITHUB_CONNECT_MESSAGES map above — these two surfaces are deliberately
1860
+ // separate so MCP failure copy can never claim the GitHub task failed.
1861
+ const UI_STRINGS = {
1862
+ // Scopes an install-instructions failure to the optional section. Prefixed
1863
+ // onto the detail resolved by getApiErrorMessage() so the user learns which
1864
+ // area failed and that the primary GitHub task is unaffected.
1865
+ mcpErrorScope: 'Editor and MCP setup instructions could not be loaded. Connecting GitHub above is unaffected and still available.',
1866
+ mcpMissingProject: 'Missing projectId — please navigate here from your project dashboard.',
1867
+ installKeyInserted: 'Admin install key generated and inserted into the snippets.',
1868
+ // Zero snippet slots: report the limitation truthfully rather than claiming
1869
+ // an insertion that did not happen (BAPI-630 / CI-2).
1870
+ installKeyNoSlots: 'Admin install key generated, but no editor snippets were available for automatic insertion. Copy the key below, or retry loading the optional editor and MCP instructions.',
1871
+ };
1872
+
1853
1873
  // Holds the plaintext minted key for the current page lifetime ONLY. Never
1854
1874
  // persisted to storage, cookies, URLs, datasets, or sent back to the server.
1855
1875
  let generatedInstallKey = null;
1856
1876
 
1877
+ // The project's repository name, initialized once from the server-rendered
1878
+ // container dataset and thereafter kept in module state. Both the GitHub
1879
+ // connect and install-key actions read it from here, so neither re-reads the
1880
+ // dataset per action and the install-instructions response never has to write
1881
+ // a value back into the DOM.
1882
+ let currentRepoName = '';
1883
+
1857
1884
  // ============================================================
1858
1885
  // Clipboard helper
1859
1886
  // ============================================================
@@ -2303,6 +2330,26 @@ function renderInstallStatus(installStatus, projectId) {
2303
2330
  // Data loading
2304
2331
  // ============================================================
2305
2332
 
2333
+ // Reveal an optional-section error. The error region lives inside the MCP
2334
+ // drawer, which is collapsed by default — so expanding the drawer through the
2335
+ // shared utility is what actually makes the message visible. Without this the
2336
+ // failure would be silent, which is the exact mode BAPI-630 removes. Only the
2337
+ // MCP drawer is touched; no GitHub control or status region is involved.
2338
+ function showMcpError(message) {
2339
+ const errorEl = document.getElementById('get-started-error');
2340
+ const msgEl = document.getElementById('get-started-error-msg');
2341
+ if (msgEl) msgEl.textContent = message;
2342
+ if (errorEl) errorEl.classList.remove('hidden');
2343
+
2344
+ const drawer = document.querySelector('#get-started-container .get-started-mcp-drawer');
2345
+ if (drawer) (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.setDrawerExpanded)(drawer, true);
2346
+ }
2347
+
2348
+ // Loads the OPTIONAL editor/MCP instructions. Scope discipline (BAPI-630): this
2349
+ // function may only touch #get-started-loading, #get-started-error, and
2350
+ // #get-started-content. It must never hide, reveal, or gate any GitHub control,
2351
+ // status, result, picker, or fallback region — a failure here is not a failure
2352
+ // of the page's primary task.
2306
2353
  async function loadInstallInstructions(projectId) {
2307
2354
  const loadingEl = document.getElementById('get-started-loading');
2308
2355
  const errorEl = document.getElementById('get-started-error');
@@ -2323,12 +2370,12 @@ async function loadInstallInstructions(projectId) {
2323
2370
 
2324
2371
  if (loadingEl) loadingEl.classList.add('hidden');
2325
2372
 
2326
- // Defensive sync: keep the container's repo dataset authoritative from
2327
- // the install-instructions payload so the install-key click handler
2328
- // reads the correct repo_name even if the server attribute was empty.
2329
- const containerEl = document.getElementById('get-started-container');
2330
- if (containerEl && data.repo_name) {
2331
- containerEl.dataset.repoName = data.repo_name;
2373
+ // Keep the shared repo-name state authoritative from the payload, but
2374
+ // never let an empty/whitespace response erase a valid server-rendered
2375
+ // value. Held in module state not written back into the dataset.
2376
+ const responseRepoName = (data.repo_name || '').trim();
2377
+ if (responseRepoName) {
2378
+ currentRepoName = responseRepoName;
2332
2379
  }
2333
2380
 
2334
2381
  const repoLabel = document.getElementById('get-started-repo-label');
@@ -2341,19 +2388,15 @@ async function loadInstallInstructions(projectId) {
2341
2388
  renderInstallStatus(data.install_status, projectId);
2342
2389
 
2343
2390
  if (contentEl) contentEl.classList.remove('hidden');
2344
-
2345
- // Render any GitHub connection return-state now that the repo dataset is
2346
- // synchronized from the install-instructions payload (BAPI-506).
2347
- renderGitHubConnectionReturnState();
2348
2391
  } catch (error) {
2349
2392
  if (loadingEl) loadingEl.classList.add('hidden');
2350
2393
  if (contentEl) contentEl.classList.add('hidden');
2351
2394
 
2352
- const msgEl = document.getElementById('get-started-error-msg');
2353
- if (msgEl) {
2354
- msgEl.textContent = (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.getApiErrorMessage)(error, 'Failed to load install instructions.');
2355
- }
2356
- if (errorEl) errorEl.classList.remove('hidden');
2395
+ // The scoped sentence is the message; the shared sanitizer's detail rides
2396
+ // along parenthetically as a diagnostic aside so it reads as an aside
2397
+ // rather than a dangling fragment of the sentence before it.
2398
+ const detail = (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.getApiErrorMessage)(error, 'Failed to load install instructions.');
2399
+ showMcpError(`${UI_STRINGS.mcpErrorScope} (${detail})`);
2357
2400
  }
2358
2401
  }
2359
2402
 
@@ -2379,11 +2422,18 @@ function setInstallKeyLoading(loading) {
2379
2422
  const btn = document.getElementById('generate-install-key-btn');
2380
2423
  const panel = document.getElementById('install-key-panel');
2381
2424
  const errorEl = document.getElementById('install-key-error');
2425
+ const statusEl = document.getElementById('install-key-status');
2382
2426
  if (loading) {
2383
2427
  if (errorEl) {
2384
2428
  errorEl.textContent = '';
2385
2429
  errorEl.classList.add('hidden');
2386
2430
  }
2431
+ // Drop any prior success/warning styling so a new attempt cannot inherit
2432
+ // the semantics of the last one. Never logs or exposes the key itself.
2433
+ if (statusEl) {
2434
+ statusEl.classList.remove('install-key-status--success', 'install-key-status--warning');
2435
+ statusEl.textContent = 'Generating the admin install key…';
2436
+ }
2387
2437
  if (btn) {
2388
2438
  btn.disabled = true;
2389
2439
  btn.textContent = 'Generating…';
@@ -2419,7 +2469,7 @@ async function handleGenerateInstallKeyClick() {
2419
2469
  // Security page, so we accept this over reusing/auto-revoking prior keys.
2420
2470
  if (generatedInstallKey) return;
2421
2471
 
2422
- const repoName = container.dataset.repoName;
2472
+ const repoName = getGitHubRepoName();
2423
2473
  if (!repoName) {
2424
2474
  showInstallKeyError('Could not generate the install key. Please try again.');
2425
2475
  return;
@@ -2478,10 +2528,21 @@ async function handleGenerateInstallKeyClick() {
2478
2528
  const resultEl = document.getElementById('install-key-result');
2479
2529
  if (resultEl) resultEl.classList.remove('hidden');
2480
2530
 
2481
- injectInstallKeyIntoSnippets(apiKey);
2531
+ // Report what actually happened. injectInstallKeyIntoSnippets returns the
2532
+ // number of slots it filled; zero means the snippets were never rendered
2533
+ // (e.g. the optional MCP fetch failed), so claiming insertion would be a
2534
+ // lie the user cannot verify (BAPI-630 / CI-2).
2535
+ const insertedSlotCount = injectInstallKeyIntoSnippets(apiKey);
2482
2536
 
2483
2537
  const statusEl = document.getElementById('install-key-status');
2484
- if (statusEl) statusEl.textContent = 'Admin install key generated and inserted into the snippets.';
2538
+ if (statusEl) {
2539
+ const inserted = insertedSlotCount > 0;
2540
+ statusEl.textContent = inserted
2541
+ ? UI_STRINGS.installKeyInserted
2542
+ : UI_STRINGS.installKeyNoSlots;
2543
+ statusEl.classList.toggle('install-key-status--success', inserted);
2544
+ statusEl.classList.toggle('install-key-status--warning', !inserted);
2545
+ }
2485
2546
 
2486
2547
  // Permanently disable the button after success to avoid repeated minting.
2487
2548
  const btn = document.getElementById('generate-install-key-btn');
@@ -2536,9 +2597,7 @@ function initializeInstallKeyGeneration(container) {
2536
2597
  // ============================================================
2537
2598
 
2538
2599
  function getGitHubRepoName() {
2539
- const container = document.getElementById('get-started-container');
2540
- const repoName = container && container.dataset ? container.dataset.repoName : '';
2541
- return (repoName || '').trim();
2600
+ return currentRepoName;
2542
2601
  }
2543
2602
 
2544
2603
  function setGitHubConnectLoading(loading) {
@@ -2756,6 +2815,11 @@ function init() {
2756
2815
  const container = document.getElementById('get-started-container');
2757
2816
  if (!container) return;
2758
2817
 
2818
+ // Seed the shared repo-name state once from the server-rendered dataset.
2819
+ // Every later read goes through module state, so the GitHub button works on
2820
+ // first paint without waiting for the optional MCP fetch (BAPI-630).
2821
+ currentRepoName = ((container.dataset && container.dataset.repoName) || '').trim();
2822
+
2759
2823
  (0,_modal_js__WEBPACK_IMPORTED_MODULE_0__.initModal)();
2760
2824
 
2761
2825
  // Click-driven install-key generation; does not mint a key on page load.
@@ -2764,6 +2828,11 @@ function init() {
2764
2828
  // Wire the "Connect GitHub" launch button (BAPI-506).
2765
2829
  initializeGitHubConnect();
2766
2830
 
2831
+ // Render any ?githubConnection=<status> return state here — NOT from the
2832
+ // install-instructions success path. A user coming back from GitHub must
2833
+ // see the outcome even when the optional MCP fetch fails (BAPI-630 / R1).
2834
+ renderGitHubConnectionReturnState();
2835
+
2767
2836
  // Bind keyboard navigation once — renderSnippetTabs may run multiple times (retry),
2768
2837
  // so binding here prevents the listener from stacking on the tabList element.
2769
2838
  const tabList = document.getElementById('editor-tabs');
@@ -2809,11 +2878,8 @@ function init() {
2809
2878
 
2810
2879
  if (!projectId) {
2811
2880
  const loadingEl = document.getElementById('get-started-loading');
2812
- const errorEl = document.getElementById('get-started-error');
2813
- const msgEl = document.getElementById('get-started-error-msg');
2814
2881
  if (loadingEl) loadingEl.classList.add('hidden');
2815
- if (msgEl) msgEl.textContent = 'Missing projectId — please navigate here from your project dashboard.';
2816
- if (errorEl) errorEl.classList.remove('hidden');
2882
+ showMcpError(UI_STRINGS.mcpMissingProject);
2817
2883
  return;
2818
2884
  }
2819
2885
 
@@ -3496,7 +3562,9 @@ __webpack_require__.r(__webpack_exports__);
3496
3562
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3497
3563
  /* harmony export */ hideModal: () => (/* binding */ hideModal),
3498
3564
  /* harmony export */ initModal: () => (/* binding */ initModal),
3499
- /* harmony export */ showModal: () => (/* binding */ showModal)
3565
+ /* harmony export */ replaceModalBody: () => (/* binding */ replaceModalBody),
3566
+ /* harmony export */ showModal: () => (/* binding */ showModal),
3567
+ /* harmony export */ showModalNode: () => (/* binding */ showModalNode)
3500
3568
  /* harmony export */ });
3501
3569
  // src/js/modal.js
3502
3570
 
@@ -3550,6 +3618,49 @@ function showModal(title, message, onConfirm = null, showCancel = false, onClose
3550
3618
  }
3551
3619
  }
3552
3620
 
3621
+ /**
3622
+ * Show the modal with a caller-built DOM node as its body.
3623
+ *
3624
+ * Additive companion to {@link showModal} for rich, structured content (e.g. the
3625
+ * SFCC ticket preview). The node is appended directly — never serialized through
3626
+ * innerHTML — so untrusted values built with textContent stay safe. The caller
3627
+ * supplies its own action controls inside `contentEl`, so the shared
3628
+ * confirm/cancel actions are hidden. `onClose` runs when the modal is dismissed,
3629
+ * letting the caller restore focus to the invoking element.
3630
+ *
3631
+ * @param {string} title - Modal header text.
3632
+ * @param {HTMLElement} contentEl - Caller-created body node.
3633
+ * @param {Function|null} onClose - Optional callback fired on dismiss.
3634
+ */
3635
+ function showModalNode(title, contentEl, onClose = null) {
3636
+ if (pageModalTitle) pageModalTitle.textContent = title;
3637
+ replaceModalBody(contentEl);
3638
+
3639
+ currentConfirmCallback = null;
3640
+ currentCloseCallback = onClose;
3641
+
3642
+ if (pageModalActions) pageModalActions.classList.add('hidden');
3643
+ if (pageModal) {
3644
+ pageModal.style.display = 'block';
3645
+ } else {
3646
+ console.error('Modal element not found.');
3647
+ }
3648
+ }
3649
+
3650
+ /**
3651
+ * Replace the modal body with a caller-built DOM node without re-showing it.
3652
+ *
3653
+ * Used to transition the same open modal between states (processing → preview →
3654
+ * success/error). Clears the previous content with textContent then appends the
3655
+ * node — never innerHTML.
3656
+ * @param {HTMLElement} contentEl
3657
+ */
3658
+ function replaceModalBody(contentEl) {
3659
+ if (!pageModalBody) return;
3660
+ pageModalBody.textContent = '';
3661
+ if (contentEl) pageModalBody.appendChild(contentEl);
3662
+ }
3663
+
3553
3664
  /**
3554
3665
  * Hides the modal dialog.
3555
3666
  */
@@ -5063,10 +5174,12 @@ __webpack_require__.r(__webpack_exports__);
5063
5174
  /* harmony export */ resetProjectDetailBaseline: () => (/* binding */ resetProjectDetailBaseline),
5064
5175
  /* harmony export */ setupConditionalVisibilityListeners: () => (/* binding */ setupConditionalVisibilityListeners),
5065
5176
  /* harmony export */ setupValidationListeners: () => (/* binding */ setupValidationListeners),
5177
+ /* harmony export */ shouldWarnForBlankCriticalPriorities: () => (/* binding */ shouldWarnForBlankCriticalPriorities),
5066
5178
  /* harmony export */ toggleCiFollowupControlsVisibility: () => (/* binding */ toggleCiFollowupControlsVisibility),
5067
5179
  /* harmony export */ toggleDeepResearchTimeoutVisibility: () => (/* binding */ toggleDeepResearchTimeoutVisibility),
5068
5180
  /* harmony export */ toggleStoryPointsFieldVisibility: () => (/* binding */ toggleStoryPointsFieldVisibility),
5069
5181
  /* harmony export */ updateCiFollowupInstructionsByteCounter: () => (/* binding */ updateCiFollowupInstructionsByteCounter),
5182
+ /* harmony export */ updateCriticalPrioritiesWarning: () => (/* binding */ updateCriticalPrioritiesWarning),
5070
5183
  /* harmony export */ validateInput: () => (/* binding */ validateInput),
5071
5184
  /* harmony export */ validateReviewPolicyOverrides: () => (/* binding */ validateReviewPolicyOverrides)
5072
5185
  /* harmony export */ });
@@ -5097,6 +5210,44 @@ const estimateScaleJsonErrorDiv = document.getElementById('estimate_scale-error'
5097
5210
  // are sent to the backend.
5098
5211
  let projectDetailBaseline = null;
5099
5212
 
5213
+ // --- BAPI-590: critical-priorities blank-configuration warning ---
5214
+ // Non-blocking signal shown when the critical-priorities check is enabled but no
5215
+ // priorities are configured (the check is silently skipped). Defined once at
5216
+ // module scope so the client- and route-side copies stay identical.
5217
+ const CRITICAL_PRIORITIES_BLANK_WARNING =
5218
+ 'Critical-priorities checking is enabled, but no priorities are configured; this check will be skipped.';
5219
+
5220
+ /**
5221
+ * Whether the blank-priorities warning condition holds: the check is enabled and
5222
+ * the priorities textarea is empty after trimming whitespace. Reads the checkbox
5223
+ * and textarea directly from the DOM and is safe to call when either control is
5224
+ * absent (returns false).
5225
+ * @returns {boolean}
5226
+ */
5227
+ function shouldWarnForBlankCriticalPriorities() {
5228
+ const toggle = document.getElementById('check_critical_priorities');
5229
+ const textarea = document.getElementById('critical_priorities');
5230
+ if (!toggle || !textarea) return false;
5231
+ return !!toggle.checked && (textarea.value || '').trim() === '';
5232
+ }
5233
+
5234
+ /**
5235
+ * Show or hide the dedicated critical-priorities warning container from the
5236
+ * current control state. Guards every DOM lookup, sets text via textContent, and
5237
+ * toggles only the `.hidden` class on the warning node (never inline display).
5238
+ */
5239
+ function updateCriticalPrioritiesWarning() {
5240
+ const warningEl = document.getElementById('critical_priorities-warning');
5241
+ if (!warningEl) return;
5242
+ if (shouldWarnForBlankCriticalPriorities()) {
5243
+ warningEl.textContent = CRITICAL_PRIORITIES_BLANK_WARNING;
5244
+ warningEl.classList.remove('hidden');
5245
+ } else {
5246
+ warningEl.textContent = '';
5247
+ warningEl.classList.add('hidden');
5248
+ }
5249
+ }
5250
+
5100
5251
  // --- Custom DOM value readers for fields the shared diff util cannot read by id ---
5101
5252
 
5102
5253
  /**
@@ -5907,10 +6058,22 @@ function setupConditionalVisibilityListeners() {
5907
6058
  reviewPolicyResetAll.addEventListener('click', handleReviewPolicyResetAll);
5908
6059
  }
5909
6060
 
5910
- // Apply initial state for all three dependent regions before baseline capture.
6061
+ // BAPI-590: refresh the non-blocking critical-priorities warning whenever the
6062
+ // check is toggled or the priorities text changes.
6063
+ const checkCriticalPriorities = document.getElementById('check_critical_priorities');
6064
+ if (checkCriticalPriorities) {
6065
+ checkCriticalPriorities.addEventListener('change', updateCriticalPrioritiesWarning);
6066
+ }
6067
+ const criticalPriorities = document.getElementById('critical_priorities');
6068
+ if (criticalPriorities) {
6069
+ criticalPriorities.addEventListener('input', updateCriticalPrioritiesWarning);
6070
+ }
6071
+
6072
+ // Apply initial state for all dependent regions before baseline capture.
5911
6073
  toggleDeepResearchTimeoutVisibility();
5912
6074
  toggleStoryPointsFieldVisibility();
5913
6075
  toggleCiFollowupControlsVisibility();
6076
+ updateCriticalPrioritiesWarning();
5914
6077
  }
5915
6078
 
5916
6079
  // Canonical descriptor list driving baseline capture, sparse diffing, section
@@ -5964,21 +6127,13 @@ const PROJECT_DETAIL_FIELD_DESCRIPTORS = [
5964
6127
  { field: 'backend_correctness_standards', type: 'text', id: 'backend_correctness_standards' },
5965
6128
  { field: 'template_correctness_standards', type: 'text', id: 'template_correctness_standards' },
5966
6129
  { field: 'style_correctness_standards', type: 'text', id: 'style_correctness_standards' },
5967
- { field: 'reviewable_file_types', type: 'array', delimiter: ',', id: 'reviewable_file_types' },
5968
- { field: 'include_path', type: 'array', delimiter: '\n', id: 'include_path' },
5969
- { field: 'exclude_path', type: 'array', delimiter: '\n', id: 'exclude_path' },
5970
- { field: 'frontend_styleguide', type: 'text', id: 'frontend_styleguide' },
5971
- { field: 'backend_styleguide', type: 'text', id: 'backend_styleguide' },
5972
- { field: 'css_styleguide', type: 'text', id: 'css_styleguide' },
5973
- { field: 'review_correctness', type: 'boolean', id: 'review_correctness' },
5974
- { field: 'review_style', type: 'boolean', id: 'review_style' },
6130
+ // BAPI-590: the simplified two-check reviewer surface (requirements check +
6131
+ // critical-priorities check/text). The legacy reviewer controls (style guides,
6132
+ // file types, include/exclude paths, correctness/style/architecture toggles,
6133
+ // test/doc generation, documentation standards, score threshold) were removed.
5975
6134
  { field: 'review_requirements', type: 'boolean', id: 'review_requirements' },
5976
- { field: 'review_architecture', type: 'boolean', id: 'review_architecture' },
5977
- { field: 'create_tests', type: 'boolean', id: 'create_tests' },
5978
- { field: 'create_documentation', type: 'boolean', id: 'create_documentation' },
5979
- { field: 'documentation_standards', type: 'text', id: 'documentation_standards' },
5980
- { field: 'score_threshold_for_review', type: 'text', id: 'score_threshold_for_review' },
5981
- { field: 'no_comment_on_passing_score', type: 'boolean', id: 'no_comment_on_passing_score' },
6135
+ { field: 'check_critical_priorities', type: 'boolean', id: 'check_critical_priorities' },
6136
+ { field: 'critical_priorities', type: 'text', id: 'critical_priorities' },
5982
6137
  ];
5983
6138
 
5984
6139
  // Maps each descriptor field to exactly one backend payload section. The four
@@ -6022,21 +6177,9 @@ const FIELD_TO_SECTION = {
6022
6177
  backend_correctness_standards: 'code_reviewer',
6023
6178
  template_correctness_standards: 'code_reviewer',
6024
6179
  style_correctness_standards: 'code_reviewer',
6025
- reviewable_file_types: 'code_reviewer',
6026
- include_path: 'code_reviewer',
6027
- exclude_path: 'code_reviewer',
6028
- frontend_styleguide: 'code_reviewer',
6029
- backend_styleguide: 'code_reviewer',
6030
- css_styleguide: 'code_reviewer',
6031
- review_correctness: 'code_reviewer',
6032
- review_style: 'code_reviewer',
6033
6180
  review_requirements: 'code_reviewer',
6034
- review_architecture: 'code_reviewer',
6035
- create_tests: 'code_reviewer',
6036
- create_documentation: 'code_reviewer',
6037
- documentation_standards: 'code_reviewer',
6038
- score_threshold_for_review: 'code_reviewer',
6039
- no_comment_on_passing_score: 'code_reviewer',
6181
+ check_critical_priorities: 'code_reviewer',
6182
+ critical_priorities: 'code_reviewer',
6040
6183
  };
6041
6184
 
6042
6185
  /**
@@ -6173,7 +6316,9 @@ const EXPLANATION_TEXTS = {
6173
6316
  'css_styleguide': 'Describe style conventions for CSS/SASS/LESS code. Optional. If blank, CSS style reviews are skipped.',
6174
6317
  'review_correctness': 'Enable/disable checking code for bugs and errors. Defaults to true. Optional.',
6175
6318
  'review_style': 'Enable/disable checking code for style guide adherence. Defaults to true. Optional.',
6176
- 'review_requirements': 'Enable/disable checking if the PR fulfills ticket requirements (requires ticket ID in branch/commit). Best for initial commits. Defaults to true. Optional.',
6319
+ 'review_requirements': 'When enabled, the code reviewer checks whether the pull request actually covers the ticket\'s stated requirements (requires a ticket ID in the branch or commit). Best for initial commits. Defaults to enabled.',
6320
+ 'check_critical_priorities': 'When enabled, the code reviewer additionally checks each pull request against the Critical Priorities you configure below. If this check is on but no priorities are configured, the check is skipped.',
6321
+ 'critical_priorities': 'The critical priorities the code reviewer holds every pull request to (e.g., "Never log secrets or PII", "All DB writes must be repo-scoped"). Leave blank to skip the critical-priorities check even when it is enabled.',
6177
6322
  'review_architecture': 'Enable/disable checking for integration errors and correctness (if correctness review is off). Defaults to true. Optional.',
6178
6323
  'create_tests': 'Enable/disable AI generation of unit/integration tests for the PR. Defaults to false. Optional.',
6179
6324
  'create_documentation': 'Enable/disable AI generation of documentation for the PR. Defaults to false. Optional.',
@@ -6468,24 +6613,18 @@ function populateProjectForms(data) {
6468
6613
  if (el) el.value = reviewer[field] || '';
6469
6614
  });
6470
6615
 
6471
- // Code Reviewer Form (hidden drawer - still populated to preserve values on save)
6616
+ // Code Reviewer Form (BAPI-590: simplified two-check surface). The legacy
6617
+ // reviewer controls were removed, so only the requirements check and the
6618
+ // critical-priorities check/text are populated here.
6472
6619
  if (codeReviewForm) {
6473
- codeReviewForm.elements.reviewable_file_types.value = (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.arrayToString)(reviewer.reviewable_file_types || []);
6474
- codeReviewForm.elements.include_path.value = (reviewer.include_path || []).join('\n');
6475
- codeReviewForm.elements.exclude_path.value = (reviewer.exclude_path || []).join('\n');
6476
- codeReviewForm.elements.frontend_styleguide.value = reviewer.frontend_styleguide || '';
6477
- codeReviewForm.elements.backend_styleguide.value = reviewer.backend_styleguide || '';
6478
- codeReviewForm.elements.css_styleguide.value = reviewer.css_styleguide || '';
6479
- codeReviewForm.elements.review_correctness.checked = reviewer.review_correctness !== undefined ? reviewer.review_correctness : true;
6480
- codeReviewForm.elements.review_style.checked = reviewer.review_style !== undefined ? reviewer.review_style : true;
6481
6620
  codeReviewForm.elements.review_requirements.checked = reviewer.review_requirements !== undefined ? reviewer.review_requirements : true;
6482
- codeReviewForm.elements.review_architecture.checked = reviewer.review_architecture !== undefined ? reviewer.review_architecture : true;
6483
- codeReviewForm.elements.create_tests.checked = reviewer.create_tests || false;
6484
- codeReviewForm.elements.create_documentation.checked = reviewer.create_documentation || false;
6485
- codeReviewForm.elements.documentation_standards.value = reviewer.documentation_standards || '';
6486
- codeReviewForm.elements.score_threshold_for_review.value = reviewer.score_threshold_for_review !== undefined ? reviewer.score_threshold_for_review : 85;
6487
- codeReviewForm.elements.no_comment_on_passing_score.checked = reviewer.no_comment_on_passing_score !== undefined ? reviewer.no_comment_on_passing_score : true;
6621
+ codeReviewForm.elements.check_critical_priorities.checked = !!reviewer.check_critical_priorities;
6622
+ codeReviewForm.elements.critical_priorities.value = reviewer.critical_priorities || '';
6488
6623
  }
6624
+
6625
+ // Surface an already-invalid persisted state (check enabled, priorities blank)
6626
+ // immediately after applying the returned reviewer values.
6627
+ updateCriticalPrioritiesWarning();
6489
6628
  }
6490
6629
 
6491
6630
  /**
@@ -6566,9 +6705,18 @@ async function handleSaveProject() {
6566
6705
  const response = await (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.apiCall)(`/setup/project-detail/${currentProjectId}`, 'PUT', projectData);
6567
6706
 
6568
6707
  if (response) {
6569
- (0,_modal_js__WEBPACK_IMPORTED_MODULE_0__.showModal)('Success', 'Project configuration saved successfully!');
6570
- // Re-snapshot from the saved values so an immediate second save with
6571
- // no further edits is clean.
6708
+ // BAPI-590: keep the adjacent priorities warning visible after save, and
6709
+ // surface the route's optional warning as a saved-with-warning result
6710
+ // rather than presenting the (successful) save as a failure.
6711
+ updateCriticalPrioritiesWarning();
6712
+ const savedWarning = typeof response.warning === 'string' ? response.warning.trim() : '';
6713
+ if (savedWarning) {
6714
+ (0,_modal_js__WEBPACK_IMPORTED_MODULE_0__.showModal)('Saved with warning', `Project configuration saved.\n\n${savedWarning}`);
6715
+ } else {
6716
+ (0,_modal_js__WEBPACK_IMPORTED_MODULE_0__.showModal)('Success', 'Project configuration saved successfully!');
6717
+ }
6718
+ // Re-snapshot from the saved values (the route-level warning accompanies a
6719
+ // successful persist) so an immediate second save with no edits is clean.
6572
6720
  resetProjectDetailBaseline();
6573
6721
  }
6574
6722
  // A null response is the centralized 401 redirect (handled by apiCall);
@@ -6723,6 +6871,11 @@ function validateAllForms() {
6723
6871
  allValid = false;
6724
6872
  }
6725
6873
 
6874
+ // BAPI-590: refresh the blank-priorities warning during pre-save validation.
6875
+ // This is a non-blocking signal only — it never sets custom validity, adds
6876
+ // `.input-error`, or flips `allValid`.
6877
+ updateCriticalPrioritiesWarning();
6878
+
6726
6879
  return allValid;
6727
6880
  }
6728
6881
 
@@ -6763,6 +6916,12 @@ function handleInputClearError(event) {
6763
6916
  if (element.id === 'ci_followup_instructions') {
6764
6917
  updateCiFollowupInstructionsByteCounter();
6765
6918
  }
6919
+ // BAPI-590: the priorities warning lives in its own container (not the
6920
+ // `-error` node), so clearing a genuine field error never erases it; refresh
6921
+ // it here so live edits keep the warning state accurate.
6922
+ if (element.id === 'critical_priorities') {
6923
+ updateCriticalPrioritiesWarning();
6924
+ }
6766
6925
  }
6767
6926
 
6768
6927
  /**