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 @@ -41,6 +41,15 @@ assert.deepEqual(replanCadenceEditorDefaults(
{ count_unit: "completed_todos", count: 4, stride_strategy: "off" },
"legacy effective intervals retain their own unit and count");
const inheritedReview = { count_unit: "effective_turns", count: 3, stride_strategy: "off" };
assert.deepEqual(goalReplanCadenceWriteValue({...inheritedReview, planning_horizon_goal: true}, false, false),
{stride_strategy: "off"}, "displaying the default Goal context does not create an override");
for (const enabled of [true, false]) {
assert.deepEqual(goalReplanCadenceWriteValue({...inheritedReview, planning_horizon_goal: enabled}, false, true),
{stride_strategy: "off", planning_horizon_goal: enabled}, "explicit horizon choices survive a form save");
}
assert.equal(replanCadenceEditorDefaults({...inheritedReview, planning_horizon_goal: false},
{...cadenceDefault, planning_horizon_goal: true}).planning_horizon_goal, false,
"a sparse Goal form displays the effective machine opt-out");
assert.deepEqual(goalReplanCadenceEditorValue({ stride_strategy: "evidence_conditioned_v1" }, inheritedReview),
{ ...inheritedReview, stride_strategy: "evidence_conditioned_v1" },
"strategy-only Goal settings display the live review interval");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export function replanCadenceEditorDefaults(effective: unknown, fallback: unknow
const live = configurationObject(replanCadenceEditorValue(effective));
if (Object.hasOwn(live, "count_unit")) defaults.count_unit = live.count_unit;
if (Object.hasOwn(live, "count")) defaults.count = live.count;
if (Object.hasOwn(live, "planning_horizon_goal")) defaults.planning_horizon_goal = live.planning_horizon_goal;
// Strategy omission keeps the existing default-off meaning.
return defaults;
}
Expand All @@ -100,12 +101,14 @@ export function hasReplanCadenceOverride(value: unknown): boolean {
export function goalReplanCadenceWriteValue(
draft: Record<string, unknown>,
cadenceOverride: boolean,
horizonOverride = Object.hasOwn(draft, "planning_horizon_goal"),
): Record<string, unknown> {
const value = { ...draft };
if (!cadenceOverride) {
delete value.count_unit;
delete value.count;
}
if (!horizonOverride) delete value.planning_horizon_goal;
return value;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@ export const outcomeRequirementsSchema = z.object({schema_version: z.literal("lo
})),
});
export type OutcomeRequirements = z.infer<typeof outcomeRequirementsSchema>;
export const goalObjectiveSchema = z.object({revision: z.number().int().positive(), digest: z.string(), objective: z.string(),
original_objective: z.string(), reason: z.string(), source_ref: z.string(),
peer_adoption: z.array(z.object({agent_id: z.string(), revision: z.number().nullable(), current: z.boolean()})),
acceptance_gaps: z.array(z.record(z.string(), z.unknown())),
});
// Both configured checks and the default outcome projection retain the native
// lifetime identity. A cached editor must not follow a reused Goal id.
const goalAcceptanceSource = {
goal_objective: goalObjectiveSchema.optional(),
lifecycle_state: z.string().optional(),
goal_ref: z.object({goal_id: z.string(), goal_instance_id: z.string()}).nullable().optional(),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ type CapabilityConfigurationFieldsProps = Readonly<{
editor: CapabilityConfigurationEditor;
enabledAction?: ReactNode;
omitKeys?: readonly string[];
advancedKeys?: readonly string[];
onChange?: FieldChange;
value: Record<string, unknown>;
}>;
Expand All @@ -126,15 +127,17 @@ export function CapabilityConfigurationFields({
editor,
enabledAction,
omitKeys = [],
advancedKeys = [],
onChange,
value,
}: CapabilityConfigurationFieldsProps) {
const idPrefix = useId();
const { locale } = useWorkspaceI18n();
const omitted = new Set(omitKeys);
const advanced = new Set(advancedKeys);
const fields = editor.fields.filter((field) => !omitted.has(field.key));

return (
<fieldset className="personal-capability-fields" disabled={disabled}>
{editor.fields.filter((field) => !omitted.has(field.key)).map((field) => {
const controls = (items: ConfigurationField[]) => items.map((field) => {
const control = <ConfigurationFieldControl
copy={copy}
field={field}
Expand All @@ -147,7 +150,14 @@ export function CapabilityConfigurationFields({
return field.key === "enabled" && field.input_kind === "boolean"
? <div className="personal-capability-enabled-row" key={field.key}>{control}{enabledAction}</div>
: control;
})}
});
return (
<fieldset className="personal-capability-fields" disabled={disabled}>
{controls(fields.filter((field) => !advanced.has(field.key)))}
{fields.some((field) => advanced.has(field.key)) ? <details className="personal-capability-help">
<summary>{locale === "zh-CN" ? "高级设置" : "Advanced settings"}</summary>
<div className="personal-capability-fields">{controls(fields.filter((field) => advanced.has(field.key)))}</div>
</details> : null}
</fieldset>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
executor_model: { label: "Model", description: "Optional model for the selected executor. Leave blank to keep the executor's own default." },
executor_reasoning_effort: { label: "Reasoning effort", description: "Optional reasoning effort for the selected executor. Leave blank to keep the executor's own default." },
count_unit: { label: "Review after", description: "Work Turns require accepted settlement; polls and retries do not count.", options: { completed_todos: "Completed Todos", effective_turns: "Settled work Turns" } },
planning_horizon_goal: { label: "Show Goal in planning horizon", description: "Default off. Read the full original Goal and acceptance before choosing work. Does not change replan, stride or execution authority." },
planning_horizon_goal: { label: "Show Goal in planning horizon", description: "Default on. Keep the original Goal and acceptance visible before choosing work. Turn off for rollback or comparisons; replan, stride and execution authority are unchanged." },
stride_strategy: { label: "Stride strategy", description: "Structured work scope, route and validation proposals are required for enabled fresh Vision reviews. Does not grant execution authority.", options: { off: "Off", evidence_conditioned_v1: "Evidence-conditioned · v1" } },
count: { label: "Number between reviews", description: "Settled work Turns: 1–6; completed Todos: 1–5. Goal overrides take precedence over device defaults." },
allowed_domains: { label: "Allowed responsibility domains", description: "Enter one bounded, public-safe domain per line." },
Expand Down Expand Up @@ -199,7 +199,7 @@ const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
executor_model: { label: "模型", description: "所选执行器使用的模型,可留空;留空表示沿用执行器自身的默认模型。" },
executor_reasoning_effort: { label: "推理档位", description: "所选执行器使用的推理档位,可留空;留空表示沿用执行器自身的默认档位。" },
count_unit: { label: "复核计数依据", description: "有效工作 Turn 须完成结算;轮询和重复重试不计数。", options: { completed_todos: "已完成 Todo", effective_turns: "已结算工作 Turn" } },
planning_horizon_goal: { label: "在规划视野中显示 Goal", description: "默认关闭。选择工作前读取完整原目标与验收要求,不改变复盘、步幅策略或执行权限。" },
planning_horizon_goal: { label: "在规划视野中显示 Goal", description: "默认开启,选择工作前可见原目标与验收要求。可为回滚或对照实验关闭;不改变复盘、步幅策略或执行权限。" },
stride_strategy: { label: "步幅策略", description: "启用后,新的 Vision 路线复盘须提交工作大小、路线及验证范围的结构化提案,不授予执行权限。", options: { off: "关闭", evidence_conditioned_v1: "依据证据调整 · v1" } },
count: { label: "两次复核间的数量", description: "已结算工作 Turn 为 1–6,已完成 Todo 为 1–5;Goal 显式设置优先于设备默认值。" },
allowed_domains: { label: "允许的职责域", description: "每行填写一个有边界、可公开的职责域。" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,19 @@ type CapabilityMutationState = Readonly<{
busy: "preview" | "apply" | null;
draft: Record<string, unknown>;
cadenceOverride: boolean;
horizonOverride: boolean;
partialWrite: GoalConfigurationPartialWrite | null;
preview: GoalConfigurationPreview | null;
}>;

function goalWriteDraft(selected: CapabilityConfigurationCatalog["capabilities"][number], draft: Record<string, unknown>, editorMode: "guided" | "json", cadenceOverride: boolean) {
function goalWriteDraft(selected: CapabilityConfigurationCatalog["capabilities"][number], draft: Record<string, unknown>, editorMode: "guided" | "json", cadenceOverride: boolean, horizonOverride: boolean) {
const defaults = selected.capability_id === "todo_replan_cadence"
? editorMode === "json" ? undefined : replanCadenceEditorDefaults(selected.effective_configuration?.configuration, selected.default)
: selected.default;
const writable = projectEditableCapabilityConfiguration(selected.configuration_editor, draft, defaults);
if (selected.capability_id === "reward_memory") return rewardMemoryWriteValue(writable);
if (selected.capability_id === "todo_replan_cadence" && editorMode === "guided") {
return goalReplanCadenceWriteValue(writable, cadenceOverride);
return goalReplanCadenceWriteValue(writable, cadenceOverride, horizonOverride);
}
if (selected.capability_id === "pull_request_review" && !Object.hasOwn(selected.current ?? {}, "owner_logins")
&& JSON.stringify(writable.owner_logins ?? []) === JSON.stringify(selected.effective_configuration?.configuration?.owner_logins ?? [])) {
Expand Down Expand Up @@ -77,6 +78,7 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
busy: null,
draft: {},
cadenceOverride: false,
horizonOverride: false,
partialWrite: null,
preview: null,
});
Expand Down Expand Up @@ -109,6 +111,7 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
selected?.default,
),
cadenceOverride: hasReplanCadenceOverride(selected?.current),
horizonOverride: Object.hasOwn(selected?.current ?? {}, "planning_horizon_goal"),
partialWrite: null,
preview: null,
});
Expand All @@ -120,7 +123,7 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
setMutation((current) => ({ ...current, busy: "preview", partialWrite: null }));
setError(null);
try {
const nextPreview = await previewGoalConfiguration(goalId, selected.capability_id, configuration === null ? null : goalWriteDraft(selected, configuration, editorMode, mutation.cadenceOverride));
const nextPreview = await previewGoalConfiguration(goalId, selected.capability_id, configuration === null ? null : goalWriteDraft(selected, configuration, editorMode, mutation.cadenceOverride, mutation.horizonOverride));
setMutation((current) => ({ ...current, preview: nextPreview }));
} catch (reason) {
setError(reason instanceof Error ? reason.message : t("capabilities.previewFailed"));
Expand All @@ -134,7 +137,7 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
setMutation((current) => ({ ...current, busy: "apply" }));
setError(null);
try {
const writableDraft = goalWriteDraft(selected, mutation.draft, editorMode, mutation.cadenceOverride);
const writableDraft = goalWriteDraft(selected, mutation.draft, editorMode, mutation.cadenceOverride, mutation.horizonOverride);
const result = await applyGoalConfiguration(
goalId,
selected.capability_id,
Expand Down Expand Up @@ -168,6 +171,7 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
? rewardMemoryScopeDraft(selected.current, value)
: { ...current.draft, [key]: value },
cadenceOverride: current.cadenceOverride || (selected?.capability_id === "todo_replan_cadence" && (key === "count_unit" || key === "count")),
horizonOverride: current.horizonOverride || (selected?.capability_id === "todo_replan_cadence" && key === "planning_horizon_goal"),
preview: null,
}));
setError(null);
Expand All @@ -186,6 +190,8 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
: current.draft,
cadenceOverride: selected.capability_id === "todo_replan_cadence" && parsed
? hasReplanCadenceOverride(parsed) : current.cadenceOverride,
horizonOverride: selected.capability_id === "todo_replan_cadence" && parsed
? Object.hasOwn(parsed, "planning_horizon_goal") : current.horizonOverride,
}));
setError(null);
}
Expand All @@ -194,7 +200,7 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
if (loading || mutation.busy || !jsonValid) return;
if (editorMode === "guided" && selected) {
const jsonValue = selected.capability_id === "todo_replan_cadence"
? goalWriteDraft(selected, mutation.draft, editorMode, mutation.cadenceOverride)
? goalWriteDraft(selected, mutation.draft, editorMode, mutation.cadenceOverride, mutation.horizonOverride)
: mutation.draft;
setJsonDraft(JSON.stringify(jsonValue, null, 2));
}
Expand Down Expand Up @@ -428,6 +434,7 @@ function CapabilityCatalog({ callbacks, catalog, goalId, loading, notification,
disabled={editorBusy}
copy={localizedCapabilityFieldCopy(locale, localizedSelected.capability_id)}
editor={localizedSelected.configuration_editor}
advancedKeys={selected.capability_id === "todo_replan_cadence" ? ["planning_horizon_goal"] : []}
omitKeys={selected.capability_id === "reward_memory"
? ["agent_id", ...(draft.agent_id ? ["enabled_agents", ...(draft.enabled === false ? ["config_path"] : [])] : ["enabled"])]
: []}
Expand Down
Loading
Loading