diff --git a/apps/presentation/dashboard/src/features/personal-workspace/capability-localization.ts b/apps/presentation/dashboard/src/features/personal-workspace/capability-localization.ts index 6d3c5c53c..02510ae15 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/capability-localization.ts +++ b/apps/presentation/dashboard/src/features/personal-workspace/capability-localization.ts @@ -105,6 +105,7 @@ const capabilityCopy: Record> = { const fieldCopy: Record = { en: { + completed_todos: { label: "Completed Todos between Goal reviews", description: "Machine default or explicit Goal override, from 1 to 5." }, allowed_domains: { label: "Allowed responsibility domains", description: "Enter one bounded, public-safe domain per line." }, coordinator_agent_id: { label: "Coordinator Agent", description: "Use an already registered Agent id; leave blank to disable coordination." }, enabled: { label: "Enabled" }, @@ -119,6 +120,7 @@ const fieldCopy: Record = { timezone: { label: "Timezone", description: "Use an IANA timezone, for example Asia/Shanghai." }, }, "zh-CN": { + completed_todos: { label: "两次 Goal 复核间的已完成 Todo 数", description: "可设置 1–5;机器默认值可被 Goal 显式覆盖。" }, allowed_domains: { label: "允许的职责域", description: "每行填写一个有边界、可公开的职责域。" }, coordinator_agent_id: { label: "协调 Agent", description: "填写一个已经注册的 Agent ID;留空表示关闭协调。" }, enabled: { label: "启用" }, diff --git a/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx b/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx index b0439d2e5..a660189f9 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx @@ -770,7 +770,7 @@ const en = { "machine.editorUnavailableDescription": "The namespace remains visible in the registry. Install or upgrade its Dashboard editor before changing it.", "machine.editorMode": "Editor mode", "machine.liveDefault": "Live default; Goal override wins", - "machine.liveDefaultDescription": "Goals without an explicit periodic-report override read this machine policy on every plan. Changes and removal affect those existing Goals immediately; an explicit Goal override stays pinned.", + "machine.liveDefaultDescription": "Goals without an explicit override read the current machine policy at the capability’s next decision point. Changes and removal affect those existing Goals immediately; an explicit Goal override stays pinned.", "machine.genericNamespaceDescription": "Edit this registered namespace as JSON. LoopX validates it with the capability-owned schema before previewing any write.", "machine.jsonConfiguration": "Namespace configuration (JSON)", "machine.jsonConfigurationHelp": "Only this namespace is updated. Other machine configuration is preserved, and Apply remains locked to the reviewed preview revision.", @@ -789,6 +789,10 @@ const en = { "machine.periodicReportDescription": "Default report policy for every Goal without an explicit override. Goal scheduling and delivery consume the effective configuration.", "machine.periodicReportActivation": "Enabled means automatic delivery at validated stage boundaries", "machine.periodicReportActivationDescription": "This is not a weekly timer. When LoopX validates a Goal stage completion, an Agent prepares and freezes the report, then automatically delivers it through the configured Goal Channel. The enabled subscription is standing delivery authority; failures and route drift stop closed for repair.", + "machine.changeQualityActivation": "Qualification is a quality gate, not new authority", + "machine.changeQualityActivationDescription": "Goals that inherit this policy qualify their exact final diff. safe_fix allows at most one bounded fix pass inside existing write authority; this setting never grants file, permission, or merge authority.", + "machine.replanCadenceActivation": "Review cadence changes timing, not execution authority", + "machine.replanCadenceActivationDescription": "Goals without an explicit override read this threshold before the next review decision. It does not create Turns, spend quota, or grant authority.", "machine.preview": "Review exact machine change", "machine.previewChanges": "Preview changes", "machine.previewError": "A machine-policy preview could not be created.", @@ -1735,7 +1739,7 @@ const zhCN: Record = { "machine.editorUnavailableDescription": "它仍会显示在 Registry 中;安装或升级对应的 Dashboard 编辑器后才能修改。", "machine.editorMode": "编辑模式", "machine.liveDefault": "实时默认值;Goal 显式覆盖优先", - "machine.liveDefaultDescription": "没有显式周期报告覆盖的 Goal 会在每次规划时读取当前机器策略。修改或移除策略会立即影响这些已有 Goal;显式 Goal 覆盖保持固定。", + "machine.liveDefaultDescription": "没有显式覆盖的 Goal 会在该能力下一次决策时读取当前机器策略。修改或移除策略会立即影响这些已有 Goal;显式 Goal 覆盖保持固定。", "machine.genericNamespaceDescription": "使用 JSON 编辑这个已注册 Namespace。LoopX 会先按 capability 自己拥有的 schema 校验,再允许预览写入。", "machine.jsonConfiguration": "Namespace 配置(JSON)", "machine.jsonConfigurationHelp": "只更新当前 Namespace;其他机器配置会保留,Apply 仍锁定到已审阅的 Preview Revision。", @@ -1754,6 +1758,10 @@ const zhCN: Record = { "machine.periodicReportDescription": "为所有未显式覆盖的 Goal 提供默认报告策略;Goal 调度与发送消费解析后的有效配置。", "machine.periodicReportActivation": "开启后将在已验证的阶段节点自动投递", "machine.periodicReportActivationDescription": "这不是每周定时器。当 LoopX 验证 Goal 已完成一个阶段时,Agent 会生成并冻结报告,随后通过配置的 Goal Channel 自动发送。启用此订阅即授予持续投递权;发送失败或路由漂移会 fail closed 并进入修复。", + "machine.changeQualityActivation": "质量验证是质量门禁,不是新增授权", + "machine.changeQualityActivationDescription": "继承该策略的 Goal 会验证最终精确 diff。safe_fix 只允许在既有写入权限内执行至多一次有界修复;此设置不会授予文件、权限或合并权。", + "machine.replanCadenceActivation": "复核周期只改变时机,不改变执行权限", + "machine.replanCadenceActivationDescription": "没有显式覆盖的 Goal 会在下一次复核决策前读取此阈值;它不会创建 Turn、消耗配额或授予权限。", "machine.preview": "审阅机器配置变更", "machine.previewChanges": "预览变更", "machine.previewError": "无法生成机器策略预览。", diff --git a/apps/presentation/dashboard/src/features/personal-workspace/machine-configuration-settings.tsx b/apps/presentation/dashboard/src/features/personal-workspace/machine-configuration-settings.tsx index fd89f499a..521ca7416 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/machine-configuration-settings.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/machine-configuration-settings.tsx @@ -18,7 +18,7 @@ import { import { projectEditableCapabilityConfiguration } from "../../data/capability-configuration"; import { CapabilityConfigurationFields } from "./capability-configuration-fields"; import { localizeCapability, localizedCapabilityFieldCopy } from "./capability-localization"; -import { canEditCapability, CapabilityCatalogNavigation, CapabilityConfigurationSummary, CapabilityDetailHeader, CapabilityEditorStatus } from "./capability-workbench"; +import { canEditCapability, CapabilityCatalogNavigation, CapabilityConfigurationSummary, CapabilityDetailHeader, CapabilityEditorStatus, orderCapabilitiesForPresentation } from "./capability-workbench"; import { useWorkspaceI18n } from "./i18n"; type CapabilityDescriptor = CapabilityConfigurationCatalog["capabilities"][number]; @@ -95,10 +95,12 @@ export function MachineConfigurationSettings() { const [error, setError] = useState(null); const [notice, setNotice] = useState(null); - const capabilities = inspection?.capability_catalog.capabilities ?? []; + const capabilities = useMemo(() => orderCapabilitiesForPresentation( + inspection?.capability_catalog.capabilities ?? [], locale, + ), [inspection, locale]); const selectedRaw = capabilities.find( (capability) => capability.capability_id === selectedCapabilityId, - ) ?? capabilities[0]; + ) ?? capabilities.find((capability) => canEditCapability(capability, "machine")) ?? capabilities[0]; const selected = selectedRaw ? localizeCapability(selectedRaw, locale) : undefined; const selectedCurrent = currentConfiguration(inspection, selected); const configured = Boolean(selected?.machine_namespace && selectedCurrent); @@ -125,9 +127,6 @@ export function MachineConfigurationSettings() { .then((next) => { if (!active) return; setInspection(next); - setSelectedCapabilityId(next.capability_catalog.capabilities.find( - (capability) => capability.available_scopes.includes("machine"), - )?.capability_id ?? ""); }) .catch((cause: unknown) => { if (active) setError(cause instanceof Error ? cause.message : t("machine.loadError")); @@ -305,6 +304,20 @@ export function MachineConfigurationSettings() { ) : null} + {selected.capability_id === "change_quality_qualification" ? ( +
+ +
{t("machine.changeQualityActivation")}

{t("machine.changeQualityActivationDescription")}

+
+ ) : null} + + {selected.capability_id === "todo_replan_cadence" ? ( +
+ +
{t("machine.replanCadenceActivation")}

{t("machine.replanCadenceActivationDescription")}

+
+ ) : null} + {editorAvailable ? <>{editorMode === "json" || !selected.configuration_editor.fields.some((field) => field.key === "enabled" && field.input_kind === "boolean") ?