Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ const capabilityCopy: Record<WorkspaceLocale, Record<string, LocalizedCopy>> = {

const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
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" },
Expand All @@ -119,6 +120,7 @@ const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
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: "启用" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand All @@ -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.",
Expand Down Expand Up @@ -1735,7 +1739,7 @@ const zhCN: Record<WorkspaceMessageKey, string> = {
"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。",
Expand All @@ -1754,6 +1758,10 @@ const zhCN: Record<WorkspaceMessageKey, string> = {
"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": "无法生成机器策略预览。",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -95,10 +95,12 @@ export function MachineConfigurationSettings() {
const [error, setError] = useState<string | null>(null);
const [notice, setNotice] = useState<string | null>(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);
Expand All @@ -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"));
Expand Down Expand Up @@ -305,6 +304,20 @@ export function MachineConfigurationSettings() {
</section>
) : null}

{selected.capability_id === "change_quality_qualification" ? (
<section className="personal-capability-behavior-note">
<ShieldCheck aria-hidden size={18} />
<div><strong>{t("machine.changeQualityActivation")}</strong><p>{t("machine.changeQualityActivationDescription")}</p></div>
</section>
) : null}

{selected.capability_id === "todo_replan_cadence" ? (
<section className="personal-capability-behavior-note">
<ShieldCheck aria-hidden size={18} />
<div><strong>{t("machine.replanCadenceActivation")}</strong><p>{t("machine.replanCadenceActivationDescription")}</p></div>
</section>
) : null}

{editorAvailable ? <>{editorMode === "json" || !selected.configuration_editor.fields.some((field) => field.key === "enabled" && field.input_kind === "boolean") ? <div className="personal-capability-editor-mode">
<button onClick={() => changeMode(editorMode === "guided" ? "json" : "guided")} type="button">
<Code2 aria-hidden size={14} />{t(editorMode === "guided" ? "machine.editJson" : "machine.backToForm")}
Expand Down
25 changes: 15 additions & 10 deletions docs/quota-allocation.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,25 +54,30 @@ depending on the executor:

## Completed-Todo Review Cadence

`execution_profile.replan_after_completed_todos` is a Goal-level integer
hyperparameter, default **5** in both standard and fine-grained Turn modes.
Set it to 2 or 3 for earlier review. Supported values are 1–5: the current
agent projection retains five recent completions, so larger values are rejected.
Restoring 5 removes the override and preserves the existing default behavior.
`execution_profile.replan_after_completed_todos` is an integer hyperparameter,
default **5** in both standard and fine-grained Turn modes. It may be set as a
live machine default, while an explicit Goal value remains pinned until it is
cleared. Set it to 2 or 3 for earlier review. Supported values are 1–5: the
current agent projection retains five recent completions, so larger values are
rejected.

```bash
# Preview, apply, and read back the Goal setting.
loopx configure-goal --goal-id example --execution-replan-after-todos 3
loopx configure-goal --goal-id example --execution-replan-after-todos 3 --execute
loopx configure-goal --goal-id example

# Restore the default cadence.
loopx configure-goal --goal-id example --execution-replan-after-todos 5 --execute
# Restore live machine-default inheritance.
loopx configure-goal --goal-id example --clear-execution-replan-after-todos --execute
```

The Dashboard's Goal configuration catalog exposes **Goal review cadence** as
an integer field through the existing preview/apply flow. This is a setting of
the built-in goal control plane, with no provider or plugin installation.
The Dashboard exposes **Goal review cadence** in both Machine Configuration and
Goal capability settings. Machine changes are revision-locked and affect every
Goal without an explicit override at its next quota/frontier evaluation. A Goal
override is a complete value, not a field merge; clearing it restores live
inheritance. Removing the machine namespace restores the capability default of
5. This is a setting of the built-in control plane, with no provider or plugin
installation. It does not create host turns, spend quota, or grant authority.

The count includes completed advancement Todos claimed by the same Agent, with
valid completion timestamps, after the latest qualifying outcome checkpoint.
Expand Down
115 changes: 98 additions & 17 deletions examples/personal-workspace-browser/fixture.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,10 @@ function multiSubagentCapability({ current } = {}) {

function goalCapability({
availability = "supported_opt_in",
defaultConfiguration,
capabilityId,
displayName,
editorScopes = ["goal"],
fields = [{ key: "enabled", label: "Enabled", description: "", input_kind: "boolean", required: false }],
readOnlyReason,
}) {
Expand All @@ -130,12 +132,12 @@ function goalCapability({
available_scopes: ["goal"],
goal_feature_id: capabilityId,
availability,
default: editable ? Object.fromEntries(fields.flatMap((field) => field.key === "enabled" ? [[field.key, false]] : [])) : {},
default: defaultConfiguration ?? (editable ? Object.fromEntries(fields.flatMap((field) => field.key === "enabled" ? [[field.key, false]] : [])) : {}),
configuration_editor: {
schema_version: "capability_configuration_editor_v0",
editable,
supported_scopes: ["goal"],
writable_scopes: editable ? ["goal"] : [],
supported_scopes: editorScopes,
writable_scopes: editable ? editorScopes : [],
fields,
...(readOnlyReason ? { read_only_reason: readOnlyReason } : {}),
},
Expand All @@ -144,10 +146,18 @@ function goalCapability({

export function goalCapabilityCatalog(multiSubagentConfiguration) {
return [
goalCapability({
capabilityId: "todo_replan_cadence",
displayName: "Goal review cadence",
editorScopes: ["machine", "goal"],
defaultConfiguration: { completed_todos: 5 },
fields: [{ key: "completed_todos", label: "Completed Todos between Goal reviews", description: "", input_kind: "number", required: true, minimum: 1, maximum: 5 }],
}),
periodicReportCapability(),
goalCapability({
capabilityId: "change_quality_qualification",
displayName: "Change quality qualification",
editorScopes: ["machine", "goal"],
fields: [
{ key: "enabled", label: "Enabled", description: "", input_kind: "boolean", required: false },
{ key: "safe_fix", label: "Allow one bounded safe-fix pass", description: "", input_kind: "boolean", required: false },
Expand Down Expand Up @@ -188,6 +198,28 @@ export function goalCapabilityCatalog(multiSubagentConfiguration) {
];
}

function withMachineConfiguration(capability, { configuration, description }) {
return {
...capability,
description,
available_scopes: ["machine", "goal"],
machine_namespace: capability.capability_id,
machine_current: configuration,
default: configuration,
effective_value_policy: "goal_override_over_live_machine_default",
effective_configuration: {
schema_version: "capability_configuration_resolution_v0",
capability_id: capability.capability_id,
source: "machine_default",
configuration,
inherited: true,
goal_override_present: false,
machine_default_present: true,
effective_revision: `sha256:${capability.capability_id}`,
},
};
}

export function startServer() {
if (packaged) {
return spawn(process.env.LOOPX_PYTHON_BIN || "python3", [
Expand Down Expand Up @@ -690,30 +722,79 @@ export async function installApi(page, { goalSubagentConfigurationEnabled = true
route_ref: "report-route",
timezone: "Asia/Shanghai",
};
const cadenceConfiguration = {
schema_version: "todo_replan_cadence_machine_defaults_v0",
completed_todos: 3,
};
const changeQualityConfiguration = {
schema_version: "change_quality_machine_defaults_v0",
enabled: true,
safe_fix: false,
strict_receipt: true,
};
const machineNamespaces = {
change_quality_qualification: changeQualityConfiguration,
periodic_report: periodicConfiguration,
todo_replan_cadence: cadenceConfiguration,
};
const goalCapabilities = goalCapabilityCatalog();
const machineConfigurationBase = {
ok: true,
available_namespaces: ["periodic_report"],
available_namespaces: ["change_quality_qualification", "periodic_report", "todo_replan_cadence"],
namespace_catalog: {
schema_version: "machine_configuration_catalog_v0",
namespaces: [{
namespace: "periodic_report",
title: "Periodic reports",
description: "Governed report defaults.",
schema_versions: ["periodic_report_machine_defaults_v0"],
configuration_template: periodicConfiguration,
template_status: "ready",
}],
namespaces: [
{
namespace: "change_quality_qualification",
title: "Change quality qualification",
description: "Live exact-diff qualification policy without added authority.",
schema_versions: ["change_quality_machine_defaults_v0"],
configuration_template: changeQualityConfiguration,
template_status: "ready",
},
{
namespace: "periodic_report",
title: "Periodic reports",
description: "Governed report defaults.",
schema_versions: ["periodic_report_machine_defaults_v0"],
configuration_template: periodicConfiguration,
template_status: "ready",
},
{
namespace: "todo_replan_cadence",
title: "Goal review cadence",
description: "Live review threshold without added turns, quota, or authority.",
schema_versions: ["todo_replan_cadence_machine_defaults_v0"],
configuration_template: cadenceConfiguration,
template_status: "ready",
},
],
},
capability_catalog: {
schema_version: "capability_configuration_catalog_v0",
capabilities: goalCapabilityCatalog().map((capability) => capability.capability_id === "periodic_report"
? periodicReportCapability({ machineCurrent: periodicConfiguration })
: capability),
capabilities: goalCapabilities.map((capability) => {
if (capability.capability_id === "periodic_report") {
return periodicReportCapability({ machineCurrent: periodicConfiguration });
}
if (capability.capability_id === "todo_replan_cadence") {
return withMachineConfiguration(capability, {
configuration: cadenceConfiguration,
description: "Live review threshold without added turns, quota, or authority.",
});
}
if (capability.capability_id === "change_quality_qualification") {
return withMachineConfiguration(capability, {
configuration: changeQualityConfiguration,
description: "Live exact-diff qualification policy without added authority.",
});
}
return capability;
}),
},
changed_namespaces: [],
machine_configuration: {
schema_version: "loopx_machine_configuration_v0",
namespaces: { periodic_report: periodicConfiguration },
namespaces: machineNamespaces,
},
};
if (url.pathname === "/api/chat/machine-configuration" && request.method() === "GET") {
Expand All @@ -740,7 +821,7 @@ export async function installApi(page, { goalSubagentConfigurationEnabled = true
changed_namespaces: [body.namespace],
machine_configuration: {
schema_version: "loopx_machine_configuration_v0",
namespaces: { periodic_report: body.namespace_configuration },
namespaces: { ...machineNamespaces, [body.namespace]: body.namespace_configuration },
},
}, status: 201 });
return;
Expand Down
Loading
Loading