@bridge_gpt/mcp-server 0.2.24 → 0.2.26

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 (36) hide show
  1. package/README.md +98 -28
  2. package/build/agents.generated.js +1 -1
  3. package/build/bridge-api-urls.js +31 -0
  4. package/build/commands.generated.js +5 -5
  5. package/build/conductor/epic-reconcile.js +7 -1
  6. package/build/conductor/epic-runtime.js +5 -0
  7. package/build/conductor-bundle-artifacts.js +802 -0
  8. package/build/conductor-bundle-cli.js +256 -0
  9. package/build/connect-github-api.js +365 -0
  10. package/build/connect-github.js +415 -0
  11. package/build/decision-page-schema.js +34 -5
  12. package/build/decision-page-template.js +117 -35
  13. package/build/docs.generated.js +2 -1
  14. package/build/doctor.js +148 -1
  15. package/build/env-flags.js +31 -0
  16. package/build/index.js +3467 -498
  17. package/build/init.js +7 -3
  18. package/build/install-bridge.js +624 -38
  19. package/build/install-doctor.js +64 -0
  20. package/build/mcp-host-config.js +521 -0
  21. package/build/mcp-host-targets.js +194 -0
  22. package/build/mcp-install-state.js +175 -0
  23. package/build/pipelines.generated.js +127 -132
  24. package/build/readme.generated.js +1 -1
  25. package/build/start-tickets.js +166 -18
  26. package/build/tool-surface-gating.js +396 -0
  27. package/build/version.generated.js +1 -1
  28. package/docs/install/github-app.md +80 -17
  29. package/docs/install/mcp-tool-integrations.md +2 -2
  30. package/package.json +5 -5
  31. package/pipelines/learn-repository.json +111 -119
  32. package/public/css/main.min.css +258 -65
  33. package/public/css/main.min.css.map +1 -1
  34. package/public/js/main.min.js +188 -92
  35. package/public/js/main.min.js.map +1 -1
  36. package/smoke-test/SMOKE-TEST.md +4 -4
@@ -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
 
@@ -5108,10 +5174,12 @@ __webpack_require__.r(__webpack_exports__);
5108
5174
  /* harmony export */ resetProjectDetailBaseline: () => (/* binding */ resetProjectDetailBaseline),
5109
5175
  /* harmony export */ setupConditionalVisibilityListeners: () => (/* binding */ setupConditionalVisibilityListeners),
5110
5176
  /* harmony export */ setupValidationListeners: () => (/* binding */ setupValidationListeners),
5177
+ /* harmony export */ shouldWarnForBlankCriticalPriorities: () => (/* binding */ shouldWarnForBlankCriticalPriorities),
5111
5178
  /* harmony export */ toggleCiFollowupControlsVisibility: () => (/* binding */ toggleCiFollowupControlsVisibility),
5112
5179
  /* harmony export */ toggleDeepResearchTimeoutVisibility: () => (/* binding */ toggleDeepResearchTimeoutVisibility),
5113
5180
  /* harmony export */ toggleStoryPointsFieldVisibility: () => (/* binding */ toggleStoryPointsFieldVisibility),
5114
5181
  /* harmony export */ updateCiFollowupInstructionsByteCounter: () => (/* binding */ updateCiFollowupInstructionsByteCounter),
5182
+ /* harmony export */ updateCriticalPrioritiesWarning: () => (/* binding */ updateCriticalPrioritiesWarning),
5115
5183
  /* harmony export */ validateInput: () => (/* binding */ validateInput),
5116
5184
  /* harmony export */ validateReviewPolicyOverrides: () => (/* binding */ validateReviewPolicyOverrides)
5117
5185
  /* harmony export */ });
@@ -5142,6 +5210,44 @@ const estimateScaleJsonErrorDiv = document.getElementById('estimate_scale-error'
5142
5210
  // are sent to the backend.
5143
5211
  let projectDetailBaseline = null;
5144
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
+
5145
5251
  // --- Custom DOM value readers for fields the shared diff util cannot read by id ---
5146
5252
 
5147
5253
  /**
@@ -5952,10 +6058,22 @@ function setupConditionalVisibilityListeners() {
5952
6058
  reviewPolicyResetAll.addEventListener('click', handleReviewPolicyResetAll);
5953
6059
  }
5954
6060
 
5955
- // 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.
5956
6073
  toggleDeepResearchTimeoutVisibility();
5957
6074
  toggleStoryPointsFieldVisibility();
5958
6075
  toggleCiFollowupControlsVisibility();
6076
+ updateCriticalPrioritiesWarning();
5959
6077
  }
5960
6078
 
5961
6079
  // Canonical descriptor list driving baseline capture, sparse diffing, section
@@ -6009,21 +6127,13 @@ const PROJECT_DETAIL_FIELD_DESCRIPTORS = [
6009
6127
  { field: 'backend_correctness_standards', type: 'text', id: 'backend_correctness_standards' },
6010
6128
  { field: 'template_correctness_standards', type: 'text', id: 'template_correctness_standards' },
6011
6129
  { field: 'style_correctness_standards', type: 'text', id: 'style_correctness_standards' },
6012
- { field: 'reviewable_file_types', type: 'array', delimiter: ',', id: 'reviewable_file_types' },
6013
- { field: 'include_path', type: 'array', delimiter: '\n', id: 'include_path' },
6014
- { field: 'exclude_path', type: 'array', delimiter: '\n', id: 'exclude_path' },
6015
- { field: 'frontend_styleguide', type: 'text', id: 'frontend_styleguide' },
6016
- { field: 'backend_styleguide', type: 'text', id: 'backend_styleguide' },
6017
- { field: 'css_styleguide', type: 'text', id: 'css_styleguide' },
6018
- { field: 'review_correctness', type: 'boolean', id: 'review_correctness' },
6019
- { 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.
6020
6134
  { field: 'review_requirements', type: 'boolean', id: 'review_requirements' },
6021
- { field: 'review_architecture', type: 'boolean', id: 'review_architecture' },
6022
- { field: 'create_tests', type: 'boolean', id: 'create_tests' },
6023
- { field: 'create_documentation', type: 'boolean', id: 'create_documentation' },
6024
- { field: 'documentation_standards', type: 'text', id: 'documentation_standards' },
6025
- { field: 'score_threshold_for_review', type: 'text', id: 'score_threshold_for_review' },
6026
- { 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' },
6027
6137
  ];
6028
6138
 
6029
6139
  // Maps each descriptor field to exactly one backend payload section. The four
@@ -6067,21 +6177,9 @@ const FIELD_TO_SECTION = {
6067
6177
  backend_correctness_standards: 'code_reviewer',
6068
6178
  template_correctness_standards: 'code_reviewer',
6069
6179
  style_correctness_standards: 'code_reviewer',
6070
- reviewable_file_types: 'code_reviewer',
6071
- include_path: 'code_reviewer',
6072
- exclude_path: 'code_reviewer',
6073
- frontend_styleguide: 'code_reviewer',
6074
- backend_styleguide: 'code_reviewer',
6075
- css_styleguide: 'code_reviewer',
6076
- review_correctness: 'code_reviewer',
6077
- review_style: 'code_reviewer',
6078
6180
  review_requirements: 'code_reviewer',
6079
- review_architecture: 'code_reviewer',
6080
- create_tests: 'code_reviewer',
6081
- create_documentation: 'code_reviewer',
6082
- documentation_standards: 'code_reviewer',
6083
- score_threshold_for_review: 'code_reviewer',
6084
- no_comment_on_passing_score: 'code_reviewer',
6181
+ check_critical_priorities: 'code_reviewer',
6182
+ critical_priorities: 'code_reviewer',
6085
6183
  };
6086
6184
 
6087
6185
  /**
@@ -6113,8 +6211,7 @@ function getChangedFieldEntries() {
6113
6211
 
6114
6212
  /**
6115
6213
  * Final payload value for a changed field. Most fields use their normalized diff
6116
- * value as-is; estimate_scale and score_threshold_for_review keep their existing
6117
- * save shaping.
6214
+ * value as-is; estimate_scale keeps its existing save shaping.
6118
6215
  * @param {string} fieldName
6119
6216
  * @param {*} changedValue - normalized value from the diff result
6120
6217
  * @param {string|null} estimateScaleValue - validated estimate-scale string
@@ -6124,9 +6221,6 @@ function getPayloadValueForField(fieldName, changedValue, estimateScaleValue = n
6124
6221
  if (fieldName === 'estimate_scale') {
6125
6222
  return estimateScaleValue;
6126
6223
  }
6127
- if (fieldName === 'score_threshold_for_review') {
6128
- return parseInt(changedValue, 10) || 85;
6129
- }
6130
6224
  if (fieldName === 'deep_research_stall_timeout_minutes') {
6131
6225
  return changedValue === '' ? null : parseInt(changedValue, 10);
6132
6226
  }
@@ -6208,23 +6302,11 @@ const EXPLANATION_TEXTS = {
6208
6302
  'pwa_overrides_directories': 'Specify the directories that contain your PWA Kit template overrides. Enter one directory path per line (e.g., `overrides`, `app/templates`, `custom-overrides`). These directories will be given special consideration when the AI generates or modifies code.',
6209
6303
  'frontend_correctness_standards': 'Describe how the AI should review frontend Javascript. Consider project standards, error handling, data validation, etc. Required.',
6210
6304
  'backend_correctness_standards': 'Describe how the AI should review backend code. Consider project standards, error handling, data validation, security, best practices',
6211
- 'reviewable_file_types': 'List the file suffixes (e.g., `js, isml, scss`) the AI should review. Required.',
6212
- 'include_path': 'Specify directory paths (one per line) to include in reviews. Prevents reviewing third-party code. Recommended.',
6213
- 'exclude_path': 'Specify directory paths (one per line) to exclude from reviews, even if they are within an included path. Recommended.',
6214
6305
  'template_correctness_standards': 'Define standards for reviewing template code (e.g., HTML, ISML), focusing on issues like accessibility. Recommended. If blank, template correctness is skipped.',
6215
6306
  'style_correctness_standards': 'Define standards for reviewing CSS/SASS/LESS code. Optional. If blank, style correctness for these files is skipped.',
6216
- 'frontend_styleguide': 'Describe style conventions for frontend Javascript. Optional. If blank, frontend style reviews are skipped.',
6217
- 'backend_styleguide': 'Describe style conventions for backend code (e.g., JSDoc, function size). Optional. If blank, backend style reviews are skipped.',
6218
- 'css_styleguide': 'Describe style conventions for CSS/SASS/LESS code. Optional. If blank, CSS style reviews are skipped.',
6219
- 'review_correctness': 'Enable/disable checking code for bugs and errors. Defaults to true. Optional.',
6220
- 'review_style': 'Enable/disable checking code for style guide adherence. Defaults to true. Optional.',
6221
- '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.',
6222
- 'review_architecture': 'Enable/disable checking for integration errors and correctness (if correctness review is off). Defaults to true. Optional.',
6223
- 'create_tests': 'Enable/disable AI generation of unit/integration tests for the PR. Defaults to false. Optional.',
6224
- 'create_documentation': 'Enable/disable AI generation of documentation for the PR. Defaults to false. Optional.',
6225
- 'documentation_standards': 'If `Create Documentation` is enabled, describe the desired documentation format and standards here. Optional.',
6226
- 'score_threshold_for_review': 'Set a score (0-100) below which the AI will perform a review. Files scoring at or above this threshold are considered passing. Defaults to 85. Optional.',
6227
- 'no_comment_on_passing_score': 'If enabled, the AI will not leave a comment on files with a passing score. If disabled, it leaves a positive comment (e.g., "Looks good!"). Defaults to true (no comment). Optional.',
6307
+ '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.',
6308
+ '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.',
6309
+ '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.',
6228
6310
  'schedule_interval': 'How frequently should the repository parsing job run? This is measured in days (e.g., every 1 day, 2 days, etc.).',
6229
6311
  'schedule_timezone': 'The timezone for scheduling repository parsing jobs. We will run this job daily at 1:00 AM according to the timezone you select.',
6230
6312
  'deep_research_enabled': 'Enable or disable AI deep research for this project. When enabled, the MCP tool can perform in-depth web research on technical topics using the default Deep Research provider. Defaults to enabled.',
@@ -6513,24 +6595,18 @@ function populateProjectForms(data) {
6513
6595
  if (el) el.value = reviewer[field] || '';
6514
6596
  });
6515
6597
 
6516
- // Code Reviewer Form (hidden drawer - still populated to preserve values on save)
6598
+ // Code Reviewer Form (BAPI-590: simplified two-check surface). The legacy
6599
+ // reviewer controls were removed, so only the requirements check and the
6600
+ // critical-priorities check/text are populated here.
6517
6601
  if (codeReviewForm) {
6518
- codeReviewForm.elements.reviewable_file_types.value = (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.arrayToString)(reviewer.reviewable_file_types || []);
6519
- codeReviewForm.elements.include_path.value = (reviewer.include_path || []).join('\n');
6520
- codeReviewForm.elements.exclude_path.value = (reviewer.exclude_path || []).join('\n');
6521
- codeReviewForm.elements.frontend_styleguide.value = reviewer.frontend_styleguide || '';
6522
- codeReviewForm.elements.backend_styleguide.value = reviewer.backend_styleguide || '';
6523
- codeReviewForm.elements.css_styleguide.value = reviewer.css_styleguide || '';
6524
- codeReviewForm.elements.review_correctness.checked = reviewer.review_correctness !== undefined ? reviewer.review_correctness : true;
6525
- codeReviewForm.elements.review_style.checked = reviewer.review_style !== undefined ? reviewer.review_style : true;
6526
6602
  codeReviewForm.elements.review_requirements.checked = reviewer.review_requirements !== undefined ? reviewer.review_requirements : true;
6527
- codeReviewForm.elements.review_architecture.checked = reviewer.review_architecture !== undefined ? reviewer.review_architecture : true;
6528
- codeReviewForm.elements.create_tests.checked = reviewer.create_tests || false;
6529
- codeReviewForm.elements.create_documentation.checked = reviewer.create_documentation || false;
6530
- codeReviewForm.elements.documentation_standards.value = reviewer.documentation_standards || '';
6531
- codeReviewForm.elements.score_threshold_for_review.value = reviewer.score_threshold_for_review !== undefined ? reviewer.score_threshold_for_review : 85;
6532
- codeReviewForm.elements.no_comment_on_passing_score.checked = reviewer.no_comment_on_passing_score !== undefined ? reviewer.no_comment_on_passing_score : true;
6603
+ codeReviewForm.elements.check_critical_priorities.checked = !!reviewer.check_critical_priorities;
6604
+ codeReviewForm.elements.critical_priorities.value = reviewer.critical_priorities || '';
6533
6605
  }
6606
+
6607
+ // Surface an already-invalid persisted state (check enabled, priorities blank)
6608
+ // immediately after applying the returned reviewer values.
6609
+ updateCriticalPrioritiesWarning();
6534
6610
  }
6535
6611
 
6536
6612
  /**
@@ -6611,9 +6687,18 @@ async function handleSaveProject() {
6611
6687
  const response = await (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.apiCall)(`/setup/project-detail/${currentProjectId}`, 'PUT', projectData);
6612
6688
 
6613
6689
  if (response) {
6614
- (0,_modal_js__WEBPACK_IMPORTED_MODULE_0__.showModal)('Success', 'Project configuration saved successfully!');
6615
- // Re-snapshot from the saved values so an immediate second save with
6616
- // no further edits is clean.
6690
+ // BAPI-590: keep the adjacent priorities warning visible after save, and
6691
+ // surface the route's optional warning as a saved-with-warning result
6692
+ // rather than presenting the (successful) save as a failure.
6693
+ updateCriticalPrioritiesWarning();
6694
+ const savedWarning = typeof response.warning === 'string' ? response.warning.trim() : '';
6695
+ if (savedWarning) {
6696
+ (0,_modal_js__WEBPACK_IMPORTED_MODULE_0__.showModal)('Saved with warning', `Project configuration saved.\n\n${savedWarning}`);
6697
+ } else {
6698
+ (0,_modal_js__WEBPACK_IMPORTED_MODULE_0__.showModal)('Success', 'Project configuration saved successfully!');
6699
+ }
6700
+ // Re-snapshot from the saved values (the route-level warning accompanies a
6701
+ // successful persist) so an immediate second save with no edits is clean.
6617
6702
  resetProjectDetailBaseline();
6618
6703
  }
6619
6704
  // A null response is the centralized 401 redirect (handled by apiCall);
@@ -6768,6 +6853,11 @@ function validateAllForms() {
6768
6853
  allValid = false;
6769
6854
  }
6770
6855
 
6856
+ // BAPI-590: refresh the blank-priorities warning during pre-save validation.
6857
+ // This is a non-blocking signal only — it never sets custom validity, adds
6858
+ // `.input-error`, or flips `allValid`.
6859
+ updateCriticalPrioritiesWarning();
6860
+
6771
6861
  return allValid;
6772
6862
  }
6773
6863
 
@@ -6808,6 +6898,12 @@ function handleInputClearError(event) {
6808
6898
  if (element.id === 'ci_followup_instructions') {
6809
6899
  updateCiFollowupInstructionsByteCounter();
6810
6900
  }
6901
+ // BAPI-590: the priorities warning lives in its own container (not the
6902
+ // `-error` node), so clearing a genuine field error never erases it; refresh
6903
+ // it here so live edits keep the warning state accurate.
6904
+ if (element.id === 'critical_priorities') {
6905
+ updateCriticalPrioritiesWarning();
6906
+ }
6811
6907
  }
6812
6908
 
6813
6909
  /**