From 4e3aa7d8b3aa3a18306e4f56c363def65f1547b9 Mon Sep 17 00:00:00 2001 From: devswha <25837994+devswha@users.noreply.github.com> Date: Thu, 17 Sep 2026 02:40:12 +0900 Subject: [PATCH] fix(settings): say which runtime features the app withholds, and why Every app session overrides four runtime settings - `mcp.discoveryMode`, `mcp.enableProjectConfig`, `tools.discoveryMode` and `astEdit.enabled`. The overrides are the right call and they stay; saying nothing about them was not. A user whose MCP servers work in the GJC CLI finds them simply absent in the app: no error, no badge, no explanation. The only mention of MCP anywhere in src/components/ was a stray comment about dropdown labels, while Settings implied configurability that does not exist. "Works in the CLI, missing in the app, no error message" is an unanswerable support question. Automation now ends with a read-only block naming each withheld feature and the reason for it. Reports, not controls: there is nothing to switch, because a session cannot switch them either - and a test asserts the block renders no control, so a later edit cannot quietly add one. Refs #161 --- .../AutomationSettingsTab.dom.bun.test.tsx | 55 +++++++++++++++++++ .../view/tabs/AutomationSettingsTab.tsx | 30 +++++++++- src/i18n/locales/de/settings.json | 10 +++- src/i18n/locales/en/settings.json | 10 +++- src/i18n/locales/fr/settings.json | 10 +++- src/i18n/locales/it/settings.json | 10 +++- src/i18n/locales/ja/settings.json | 10 +++- src/i18n/locales/ko/settings.json | 10 +++- src/i18n/locales/ru/settings.json | 10 +++- src/i18n/locales/tr/settings.json | 10 +++- src/i18n/locales/zh-CN/settings.json | 10 +++- src/i18n/locales/zh-TW/settings.json | 10 +++- 12 files changed, 174 insertions(+), 11 deletions(-) diff --git a/src/components/settings/view/tabs/AutomationSettingsTab.dom.bun.test.tsx b/src/components/settings/view/tabs/AutomationSettingsTab.dom.bun.test.tsx index 386f5cb7..6769c873 100644 --- a/src/components/settings/view/tabs/AutomationSettingsTab.dom.bun.test.tsx +++ b/src/components/settings/view/tabs/AutomationSettingsTab.dom.bun.test.tsx @@ -318,3 +318,58 @@ test('browser routing wording keys have parity across all ten settings locales', for (const [key, text] of Object.entries(actual)) assert.ok(text.trim().length > 0, `${locale}${key}`); } }); + +/* + * The app overrides `mcp.discoveryMode`, `mcp.enableProjectConfig`, + * `tools.discoveryMode` and `astEdit.enabled` for every session. The overrides + * are the right call; saying nothing about them was not. "Works in the CLI, + * missing in the app, no error message" is an unanswerable support question. + */ + +test('the withheld runtime features are reported with a reason for each', async () => { + fakeApi(); + await mount(); + await waitFor(() => assert.equal(backendSelect().disabled, false)); + + assert.ok(screen.getByText(english.automation.withheld)); + assert.ok(screen.getByText(english.automation.withheldDescription)); + + for (const [label, reason] of [ + [english.automation.withheldMcp, english.automation.withheldMcpReason], + [english.automation.withheldToolDiscovery, english.automation.withheldToolDiscoveryReason], + [english.automation.withheldAstEdit, english.automation.withheldAstEditReason], + ]) { + assert.ok(screen.getByText(label), label); + assert.ok(screen.getByText(reason), reason); + } +}); + +test('the withheld block offers nothing to switch on', async () => { + fakeApi(); + await mount(); + await waitFor(() => assert.equal(backendSelect().disabled, false)); + + // A session cannot turn these on either, so a control here would be a lie. + const row = screen.getByText(english.automation.withheldMcp).closest('div')?.parentElement; + assert.ok(row); + assert.equal(row.querySelector('button, input, select'), null); +}); + +test('withheld-feature wording keys have parity across all ten settings locales', () => { + const keys = [ + 'withheld', 'withheldDescription', + 'withheldMcp', 'withheldMcpReason', + 'withheldToolDiscovery', 'withheldToolDiscoveryReason', + 'withheldAstEdit', 'withheldAstEditReason', + ] as const; + + for (const locale of ['en', 'ko', 'de', 'fr', 'it', 'ja', 'ru', 'tr', 'zh-CN', 'zh-TW']) { + const file = new URL(`../../../../i18n/locales/${locale}/settings.json`, import.meta.url); + const translated = JSON.parse(readFileSync(file, 'utf8')) as { automation?: Record }; + for (const key of keys) { + const text = translated.automation?.[key]; + assert.equal(typeof text, 'string', `${locale}.automation.${key}`); + assert.ok(String(text).trim().length > 0, `${locale}.automation.${key}`); + } + } +}); diff --git a/src/components/settings/view/tabs/AutomationSettingsTab.tsx b/src/components/settings/view/tabs/AutomationSettingsTab.tsx index 944ddbb5..127b42bb 100644 --- a/src/components/settings/view/tabs/AutomationSettingsTab.tsx +++ b/src/components/settings/view/tabs/AutomationSettingsTab.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from 'react'; -import { ExternalLink, RefreshCw, ShieldCheck, Trash2 } from 'lucide-react'; +import { ExternalLink, RefreshCw, ShieldCheck, SquareSlash, Trash2 } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { useAppShellStore } from '../../../../stores/useAppShellStore'; @@ -306,6 +306,34 @@ export default function AutomationSettingsTab() { ))} + + {/* + * The app overrides four runtime settings for every session: + * `mcp.discoveryMode`, `mcp.enableProjectConfig`, `tools.discoveryMode` + * and `astEdit.enabled`. Those overrides are a deliberate boundary and + * they stay - but until now nothing said so. A user whose MCP servers + * work in the GJC CLI found them simply absent here, with no error and + * no explanation, which is an unanswerable support question. + * + * Reports, not controls: there is nothing to toggle, because the point + * is that a session cannot toggle them either. + */} + + + {([ + ['mcp', 'automation.withheldMcp', 'automation.withheldMcpReason'], + ['toolDiscovery', 'automation.withheldToolDiscovery', 'automation.withheldToolDiscoveryReason'], + ['astEdit', 'automation.withheldAstEdit', 'automation.withheldAstEditReason'], + ] as const).map(([key, label, reason]) => ( + + + + {t('automation.notInstalled')} + + + ))} + + ); } diff --git a/src/i18n/locales/de/settings.json b/src/i18n/locales/de/settings.json index 5e9564f4..0e767e8f 100644 --- a/src/i18n/locales/de/settings.json +++ b/src/i18n/locales/de/settings.json @@ -169,7 +169,15 @@ "noGrants": "Keine gespeicherten Automatisierungsgenehmigungen.", "origin": "Herkunft der Website", "application": "Desktop-Anwendung", - "revoke": "Genehmigung entfernen" + "revoke": "Genehmigung entfernen", + "withheld": "In der App deaktivierte Laufzeitfunktionen", + "withheldDescription": "Die App bestimmt ihren eigenen Werkzeugsatz. Unabhängig von Ihrer GJC-Konfiguration bleiben diese Laufzeitfunktionen in jeder Sitzung aus.", + "withheldMcp": "MCP-Server", + "withheldMcpReason": "Server aus Ihren GJC-Einstellungen und eine .mcp.json im Projekt werden nicht geladen, damit keine Werkzeuge in eine Sitzung gelangen, die die App nie gewählt hat.", + "withheldToolDiscovery": "Werkzeugsuche", + "withheldToolDiscoveryReason": "Sitzungen können zur Laufzeit keine weiteren Werkzeuge aktivieren; der von der App gewählte Satz ist der gesamte Satz.", + "withheldAstEdit": "AST-Bearbeitung", + "withheldAstEditReason": "Sie zeigt Änderungen nur als Vorschau und wendet sie über ein Werkzeug an, das die App nicht bereitstellt — sie würde also Änderungen anbieten, die eine Sitzung nie übernehmen könnte." }, "notifications": { "title": "Hinweise", diff --git a/src/i18n/locales/en/settings.json b/src/i18n/locales/en/settings.json index 8bbbee4a..ea1bef88 100644 --- a/src/i18n/locales/en/settings.json +++ b/src/i18n/locales/en/settings.json @@ -184,7 +184,15 @@ "noGrants": "No saved automation approvals.", "origin": "Site origin", "application": "Desktop application", - "revoke": "Remove approval" + "revoke": "Remove approval", + "withheld": "Withheld runtime features", + "withheldDescription": "The app decides its own tool set. These runtime features stay off for every session, whatever your GJC configuration says.", + "withheldMcp": "MCP servers", + "withheldMcpReason": "Servers from your GJC settings and a project .mcp.json are not loaded, so tools the app never chose cannot reach a session.", + "withheldToolDiscovery": "Tool discovery", + "withheldToolDiscoveryReason": "Sessions cannot activate additional tools at runtime; the app-selected set is the whole set.", + "withheldAstEdit": "AST edit", + "withheldAstEditReason": "It only previews rewrites and applies them through a tool the app does not expose, so it would advertise edits a session could never commit." }, "notifications": { "title": "Notifications", diff --git a/src/i18n/locales/fr/settings.json b/src/i18n/locales/fr/settings.json index fb145f05..e5877420 100644 --- a/src/i18n/locales/fr/settings.json +++ b/src/i18n/locales/fr/settings.json @@ -169,7 +169,15 @@ "noGrants": "Aucune approbation d’automatisation enregistrée.", "origin": "Origine du site", "application": "Application de bureau", - "revoke": "Supprimer l'approbation" + "revoke": "Supprimer l'approbation", + "withheld": "Fonctions du runtime désactivées dans l'application", + "withheldDescription": "L'application décide de son propre jeu d'outils. Quelle que soit votre configuration GJC, ces fonctions du runtime restent désactivées dans toutes les sessions.", + "withheldMcp": "Serveurs MCP", + "withheldMcpReason": "Les serveurs de vos réglages GJC et un .mcp.json de projet ne sont pas chargés, afin qu'aucun outil que l'application n'a pas choisi n'atteigne une session.", + "withheldToolDiscovery": "Découverte d'outils", + "withheldToolDiscoveryReason": "Une session ne peut pas activer d'outils supplémentaires en cours d'exécution ; le jeu choisi par l'application est le jeu complet.", + "withheldAstEdit": "Édition AST", + "withheldAstEditReason": "Elle ne fait que prévisualiser les réécritures et les applique via un outil que l'application n'expose pas : elle annoncerait donc des modifications qu'une session ne pourrait jamais valider." }, "notifications": { "title": "Alertes", diff --git a/src/i18n/locales/it/settings.json b/src/i18n/locales/it/settings.json index 29a3660a..c0675b58 100644 --- a/src/i18n/locales/it/settings.json +++ b/src/i18n/locales/it/settings.json @@ -169,7 +169,15 @@ "noGrants": "Nessuna approvazione di automazione salvata.", "origin": "Origine del sito", "application": "Applicazione desktop", - "revoke": "Rimuovi l'approvazione" + "revoke": "Rimuovi l'approvazione", + "withheld": "Funzioni del runtime disattivate nell'app", + "withheldDescription": "L'app decide il proprio insieme di strumenti. Qualunque sia la tua configurazione GJC, queste funzioni del runtime restano disattivate in ogni sessione.", + "withheldMcp": "Server MCP", + "withheldMcpReason": "I server dalle tue impostazioni GJC e un .mcp.json di progetto non vengono caricati, così nessuno strumento che l'app non ha scelto raggiunge una sessione.", + "withheldToolDiscovery": "Rilevamento strumenti", + "withheldToolDiscoveryReason": "Le sessioni non possono attivare altri strumenti in esecuzione; l'insieme scelto dall'app è l'insieme completo.", + "withheldAstEdit": "Modifica AST", + "withheldAstEditReason": "Si limita ad anteprimare le riscritture e le applica tramite uno strumento che l'app non espone, quindi proporrebbe modifiche che una sessione non potrebbe mai confermare." }, "notifications": { "title": "Avvisi", diff --git a/src/i18n/locales/ja/settings.json b/src/i18n/locales/ja/settings.json index c41d2101..9e47e0bb 100644 --- a/src/i18n/locales/ja/settings.json +++ b/src/i18n/locales/ja/settings.json @@ -169,7 +169,15 @@ "noGrants": "保存された自動化の承認はありません。", "origin": "サイトのオリジン", "application": "デスクトップアプリケーション", - "revoke": "承認を削除" + "revoke": "承認を削除", + "withheld": "アプリで無効なランタイム機能", + "withheldDescription": "ツールセットはアプリが決定します。GJC の設定にかかわらず、以下のランタイム機能はすべてのセッションで無効です。", + "withheldMcp": "MCP サーバー", + "withheldMcpReason": "GJC 設定のサーバーとプロジェクトの .mcp.json は読み込まれません。アプリが選んでいないツールがセッションに届かないようにします。", + "withheldToolDiscovery": "ツール探索", + "withheldToolDiscoveryReason": "セッションは実行中に追加のツールを有効化できません。アプリが選んだ集合がすべてです。", + "withheldAstEdit": "AST 編集", + "withheldAstEditReason": "書き換えをプレビューするだけで、適用にはアプリが公開していないツールを使うため、セッションが決して確定できない編集を提示することになります。" }, "notifications": { "title": "通知", diff --git a/src/i18n/locales/ko/settings.json b/src/i18n/locales/ko/settings.json index 426ee22d..8a03a498 100644 --- a/src/i18n/locales/ko/settings.json +++ b/src/i18n/locales/ko/settings.json @@ -169,7 +169,15 @@ "noGrants": "저장된 자동화 승인 없음.", "origin": "사이트 원본", "application": "데스크톱 애플리케이션", - "revoke": "승인 취소" + "revoke": "승인 취소", + "withheld": "앱에서 비활성화된 런타임 기능", + "withheldDescription": "앱이 자체 도구 집합을 결정합니다. GJC 설정과 무관하게 아래 런타임 기능은 모든 세션에서 꺼져 있습니다.", + "withheldMcp": "MCP 서버", + "withheldMcpReason": "GJC 설정의 서버와 프로젝트의 .mcp.json을 불러오지 않습니다. 앱이 선택하지 않은 도구가 세션에 닿지 못하게 합니다.", + "withheldToolDiscovery": "도구 탐색", + "withheldToolDiscoveryReason": "세션이 실행 중에 도구를 추가로 활성화할 수 없습니다. 앱이 선택한 집합이 전부입니다.", + "withheldAstEdit": "AST 편집", + "withheldAstEditReason": "수정안을 미리 보여주기만 하고 적용은 앱이 노출하지 않는 도구로 합니다. 세션이 절대 커밋할 수 없는 편집을 광고하게 됩니다." }, "notifications": { "title": "알림 메시지", diff --git a/src/i18n/locales/ru/settings.json b/src/i18n/locales/ru/settings.json index 287cc206..ecae1c8f 100644 --- a/src/i18n/locales/ru/settings.json +++ b/src/i18n/locales/ru/settings.json @@ -169,7 +169,15 @@ "noGrants": "Нет сохраненных разрешений на автоматизацию.", "origin": "Происхождение сайта", "application": "Настольное приложение", - "revoke": "Удалить одобрение" + "revoke": "Удалить одобрение", + "withheld": "Отключённые в приложении возможности среды", + "withheldDescription": "Приложение само определяет набор инструментов. Независимо от вашей конфигурации GJC эти возможности среды отключены во всех сессиях.", + "withheldMcp": "Серверы MCP", + "withheldMcpReason": "Серверы из ваших настроек GJC и файл .mcp.json проекта не загружаются, чтобы в сессию не попали инструменты, которых приложение не выбирало.", + "withheldToolDiscovery": "Поиск инструментов", + "withheldToolDiscoveryReason": "Сессия не может включать дополнительные инструменты во время работы: выбранный приложением набор — это весь набор.", + "withheldAstEdit": "Редактирование AST", + "withheldAstEditReason": "Оно лишь показывает предпросмотр правок и применяет их инструментом, которого приложение не предоставляет, — то есть предлагало бы правки, которые сессия никогда не смогла бы применить." }, "notifications": { "title": "Центр уведомлений", diff --git a/src/i18n/locales/tr/settings.json b/src/i18n/locales/tr/settings.json index 86bf412a..9cc2c177 100644 --- a/src/i18n/locales/tr/settings.json +++ b/src/i18n/locales/tr/settings.json @@ -169,7 +169,15 @@ "noGrants": "Kaydedilmiş otomasyon onayı yok.", "origin": "Sitenin kökeni", "application": "Masaüstü uygulaması", - "revoke": "Onayı kaldır" + "revoke": "Onayı kaldır", + "withheld": "Uygulamada devre dışı çalışma zamanı özellikleri", + "withheldDescription": "Araç kümesine uygulama karar verir. GJC yapılandırmanız ne olursa olsun bu çalışma zamanı özellikleri her oturumda kapalıdır.", + "withheldMcp": "MCP sunucuları", + "withheldMcpReason": "GJC ayarlarınızdaki sunucular ve projedeki .mcp.json yüklenmez; böylece uygulamanın seçmediği araçlar bir oturuma ulaşamaz.", + "withheldToolDiscovery": "Araç keşfi", + "withheldToolDiscoveryReason": "Oturumlar çalışırken ek araç etkinleştiremez; uygulamanın seçtiği küme kümenin tamamıdır.", + "withheldAstEdit": "AST düzenleme", + "withheldAstEditReason": "Yalnızca yeniden yazımları önizler ve bunları uygulamanın sunmadığı bir araçla uygular; yani oturumun asla işleyemeyeceği düzenlemeleri duyurur." }, "notifications": { "title": "Uygulama bildirimleri", diff --git a/src/i18n/locales/zh-CN/settings.json b/src/i18n/locales/zh-CN/settings.json index 3c4b5f86..361e92af 100644 --- a/src/i18n/locales/zh-CN/settings.json +++ b/src/i18n/locales/zh-CN/settings.json @@ -169,7 +169,15 @@ "noGrants": "没有保存的自动化批准。", "origin": "站点来源", "application": "桌面应用", - "revoke": "移除批准" + "revoke": "移除批准", + "withheld": "应用中停用的运行时功能", + "withheldDescription": "工具集由应用决定。无论你的 GJC 配置如何,以下运行时功能在所有会话中都保持关闭。", + "withheldMcp": "MCP 服务器", + "withheldMcpReason": "不会加载你 GJC 设置中的服务器和项目里的 .mcp.json,以免应用从未选择的工具进入会话。", + "withheldToolDiscovery": "工具发现", + "withheldToolDiscoveryReason": "会话无法在运行时激活额外工具;应用选定的集合就是全部集合。", + "withheldAstEdit": "AST 编辑", + "withheldAstEditReason": "它只预览改写,并通过应用未开放的工具来应用,因此会展示会话永远无法提交的编辑。" }, "notifications": { "title": "通知", diff --git a/src/i18n/locales/zh-TW/settings.json b/src/i18n/locales/zh-TW/settings.json index 4c0aef85..cefc93b6 100644 --- a/src/i18n/locales/zh-TW/settings.json +++ b/src/i18n/locales/zh-TW/settings.json @@ -169,7 +169,15 @@ "noGrants": "無存儲自動化批准。", "origin": "網站原始碼", "application": "桌面應用程式", - "revoke": "移除批准" + "revoke": "移除批准", + "withheld": "應用程式中停用的執行階段功能", + "withheldDescription": "工具集由應用程式決定。無論你的 GJC 設定為何,以下執行階段功能在所有工作階段中都保持關閉。", + "withheldMcp": "MCP 伺服器", + "withheldMcpReason": "不會載入你 GJC 設定中的伺服器與專案中的 .mcp.json,以免應用程式從未選擇的工具進入工作階段。", + "withheldToolDiscovery": "工具探索", + "withheldToolDiscoveryReason": "工作階段無法在執行期間啟用額外工具;應用程式選定的集合就是全部集合。", + "withheldAstEdit": "AST 編輯", + "withheldAstEditReason": "它只預覽改寫,並透過應用程式未開放的工具套用,因此會展示工作階段永遠無法提交的編輯。" }, "notifications": { "title": "通知",