Make workspace, agent, and schedule automation consistent (#2186)

* feat(workspaces): align agent and schedule automation

Make workspace identity the shared placement boundary for MCP and CLI, while caller identity determines parentage. Keep heartbeats minimal and cron-based without changing legacy rolling interval semantics.

* test(cli): expect canonical schedule cadence

* fix(automation): preserve workspace and schedule compatibility

* fix(automation): preserve compatibility edges

* fix(workspaces): align MCP lifecycle resolution

* fix(workspaces): preserve creation intent

* fix(workspaces): preserve branch and schedule identity
This commit is contained in:
Mohamed Boudra
2026-07-18 14:04:48 +02:00
committed by GitHub
parent 98f6611362
commit ffe76a7e57
66 changed files with 2791 additions and 924 deletions
@@ -6,6 +6,7 @@ import {
formatCadence,
formatNextRun,
isNewAgentSchedule,
scheduleProductName,
partsToEveryMs,
resolveScheduleTitle,
validateCron,
@@ -54,6 +55,11 @@ describe("schedule title helpers", () => {
expect(isNewAgentSchedule(createSchedule({ targetType: "agent" }))).toBe(false);
});
it("labels engine records by product meaning", () => {
expect(scheduleProductName(createSchedule({ targetType: "new-agent" }))).toBe("Schedule");
expect(scheduleProductName(createSchedule({ targetType: "agent" }))).toBe("Heartbeat");
});
it("resolves display titles by name, config title, prompt, then fallback", () => {
expect(
resolveScheduleTitle(createSchedule({ name: "Morning run", title: "Config title" })),
+5 -1
View File
@@ -28,6 +28,10 @@ export function isNewAgentSchedule(schedule: ScheduleSummary): boolean {
return schedule.target.type === "new-agent";
}
export function scheduleProductName(schedule: ScheduleSummary): "Heartbeat" | "Schedule" {
return schedule.target.type === "agent" ? "Heartbeat" : "Schedule";
}
export function resolveScheduleTitle(schedule: ScheduleSummary): string {
const name = schedule.name?.trim();
if (name) {
@@ -43,7 +47,7 @@ export function resolveScheduleTitle(schedule: ScheduleSummary): string {
.split("\n")
.map((line) => line.trim())
.find((line) => line.length > 0);
return firstPromptLine || "Untitled schedule";
return firstPromptLine || `Untitled ${scheduleProductName(schedule).toLowerCase()}`;
}
function pluralize(value: number, noun: string): string {