Skip to content

Add cloud workflow schedule CLI#831

Merged
khaliqgant merged 4 commits intomainfrom
codex/cloud-workflow-schedule-cli
May 10, 2026
Merged

Add cloud workflow schedule CLI#831
khaliqgant merged 4 commits intomainfrom
codex/cloud-workflow-schedule-cli

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

@khaliqgant khaliqgant commented May 9, 2026

Summary

  • Add @agent-relay/cloud helpers for creating and listing Cloud workflow schedules
  • Re-export scheduleWorkflow, listWorkflowSchedules, and schedule types from @agent-relay/sdk/workflows so products like Ricky can reuse the Relay SDK surface
  • Add agent-relay cloud schedule for cron or one-time scheduled workflow launches
  • Add agent-relay cloud schedules to list schedules that also appear in the Cloud dashboard
  • Keep schedules repeatable by sending workflow content only, without one-time local code sync fields

Usage

agent-relay cloud schedule workflows/eval.yaml --cron "0 * * * *" --name "Hourly eval"
agent-relay cloud schedule workflows/eval.yaml --at 2026-05-10T09:00:00Z --name "One-off eval"
agent-relay cloud schedules

SDK:

import { scheduleWorkflow, listWorkflowSchedules } from "@agent-relay/sdk/workflows";

Companion Cloud PR: https://github.com/AgentWorkforce/cloud/pull/511
Companion Ricky CLI PR: AgentWorkforce/ricky#85

Verification

  • npm run test -- packages/cloud/src/workflows.test.ts src/cli/commands/cloud.test.ts
  • npm run -w @agent-relay/cloud build
  • npm run -w @agent-relay/sdk build

@khaliqgant khaliqgant requested a review from willwashburn as a code owner May 9, 2026 19:30
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds cloud workflow scheduling functionality to the Agent Relay CLI. It introduces two new subcommands (cloud schedule and cloud schedules) to create and list repeatable workflow schedules, backed by new Cloud API functions and strongly-typed data structures for schedule metadata.

Changes

Cloud Workflow Schedule CLI

Layer / File(s) Summary
Schedule Data Types
packages/cloud/src/types.ts
Introduces WorkflowSchedule (persisted schedule metadata with lifecycle timestamps) and ScheduleWorkflowOptions (scheduling request parameters supporting cron/one-time and timezone).
Public API Exports
packages/cloud/src/index.ts
Re-exports scheduleWorkflow, listWorkflowSchedules, WorkflowSchedule, and ScheduleWorkflowOptions to make scheduling capabilities available via the public Cloud module API.
Cloud API Implementation
packages/cloud/src/workflows.ts
Implements scheduleWorkflow (validates cron/at exclusivity, resolves workflow, builds request, posts to schedules endpoint) and listWorkflowSchedules (fetches and filters schedules). Adds type guards for response validation.
Cloud API Tests
packages/cloud/src/workflows.test.ts
Tests verify scheduleWorkflow creates correct request payloads with schedule type and optional fields, rejects invalid option combos and bad timestamps, and listWorkflowSchedules returns filtered arrays from the API endpoint.
CLI Command Implementation
src/cli/commands/cloud.ts
Registers cloud schedule (creates schedules with cron/one-time options, supports JSON output) and cloud schedules (lists schedules with formatted/JSON output). Includes schedule formatting helpers and telemetry fields.
CLI Command Tests
src/cli/commands/cloud.test.ts
Tests validate both commands invoke correct Cloud API functions, log schedule identifiers, and handle mocked responses appropriately.
SDK Re-exports & Dependency
packages/sdk/src/workflows/cloud-schedules.ts, packages/sdk/src/workflows/index.ts, packages/sdk/package.json
Adds a thin SDK re-export module for cloud schedule helpers and pins @agent-relay/cloud dependency.
Trajectory Metadata
.trajectories/completed/2026-05/*, .trajectories/index.json
Adds completed trajectory JSON/MD records documenting the schedule CLI work and SDK exposure decision, and updates the trajectories index with new entries.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • willwashburn

Poem

🐰 Hoppity-hop, schedules now hop,

Cloud workflows repeat and never stop,
schedule to run, schedules to see,
A rabbit cheers for automation glee! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding CLI support for cloud workflow scheduling, which aligns with the primary objective and changeset.
Description check ✅ Passed The description includes all required template sections with comprehensive details: a detailed summary of changes, usage examples, SDK usage, and verification steps. It exceeds the basic template requirements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/cloud-workflow-schedule-cli

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment thread .trajectories/index.json Outdated
"status": "completed",
"startedAt": "2026-05-09T19:26:42.106Z",
"completedAt": "2026-05-09T19:29:18.024Z",
"path": "/Users/khaliqgant/Projects/AgentWorkforce/relay/.trajectories/completed/2026-05/traj_u4ixmbqqm2y1.json"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Trajectory index entry uses absolute local-machine path instead of relative path

The new trajectory entry for traj_u4ixmbqqm2y1 uses an absolute path (/Users/khaliqgant/Projects/AgentWorkforce/relay/.trajectories/completed/2026-05/traj_u4ixmbqqm2y1.json) while every other entry in the file uses a relative path (e.g., .trajectories/completed/2026-05/traj_lieyyspidhfj.json). This breaks portability — any tooling that resolves trajectory paths will fail on other developer machines or in CI, since the path references a specific user's macOS home directory.

Suggested change
"path": "/Users/khaliqgant/Projects/AgentWorkforce/relay/.trajectories/completed/2026-05/traj_u4ixmbqqm2y1.json"
"path": ".trajectories/completed/2026-05/traj_u4ixmbqqm2y1.json"
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread packages/cloud/src/workflows.ts Outdated
if (hasCron) {
requestBody.cron_expression = options.cron?.trim();
} else {
requestBody.scheduled_at = new Date(String(options.at)).toISOString();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Invalid --at date string throws unhelpful RangeError instead of user-facing message

When options.at contains an unparseable date string (e.g., "next tuesday"), new Date(String(options.at)) creates an Invalid Date object, and calling .toISOString() on it throws RangeError: Invalid time value. The user gets a cryptic runtime error instead of a helpful message like "Invalid date format for --at". The hasAt guard on line 664 only checks that the string is non-empty, not that it's a valid date.

Suggested change
requestBody.scheduled_at = new Date(String(options.at)).toISOString();
const parsedDate = new Date(String(options.at));
if (Number.isNaN(parsedDate.getTime())) {
throw new Error(`Invalid date for --at: ${options.at}`);
}
requestBody.scheduled_at = parsedDate.toISOString();
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
packages/cloud/src/workflows.test.ts (1)

430-528: ⚡ Quick win

Add tests for the one-time schedule path and invalid option combinations.

Current coverage is good for cron/list, but adding at success and cron+at / neither assertions would lock in the core scheduling contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cloud/src/workflows.test.ts` around lines 430 - 528, Add tests
covering the one-time schedule path and invalid option combinations for
scheduleWorkflow/listWorkflowSchedules: create a new spec that mirrors the
existing "creates a cron schedule..." test but calls scheduleWorkflow with the
at option (e.g., at: '2026-01-01T00:00:00Z') and assert the
authorizedApiFetchMock request body indicates a one-time schedule_type (or
equivalent) and that workflowRequest includes/omits runId/s3CodeKey as
appropriate; then add two negative tests that call scheduleWorkflow with both
cron and at and with neither cron nor at and assert they throw or reject (use
the same test harness and authorizedApiFetchMock patterns used in the file).
Reference scheduleWorkflow and listWorkflowSchedules and reuse
authorizedApiFetchMock/scheduleBodies setup and assertions style from the
existing cron test.
src/cli/commands/cloud.test.ts (1)

118-171: ⚡ Quick win

Add coverage for one-time scheduling (--at) to match PR scope.

Current tests validate cron schedules and listing, but PR usage/docs also include one-time scheduling. Adding a focused --at test will close that gap and prevent regressions on the second scheduling mode.

Proposed test addition
+  it('schedule creates one-time workflow schedules', async () => {
+    const { program, deps } = createHarness();
+    cloudMocks.scheduleWorkflow.mockResolvedValueOnce({
+      id: 'sched-2',
+      name: 'One-off eval',
+      scheduleType: 'at',
+      status: 'active',
+      lastTriggeredRunId: null,
+    });
+
+    await program.parseAsync([
+      'node',
+      'agent-relay',
+      'cloud',
+      'schedule',
+      'workflow.yaml',
+      '--at',
+      '2026-05-10T09:00:00Z',
+      '--name',
+      'One-off eval',
+    ]);
+
+    expect(cloudMocks.scheduleWorkflow).toHaveBeenCalledWith(
+      'workflow.yaml',
+      expect.objectContaining({
+        at: '2026-05-10T09:00:00Z',
+        name: 'One-off eval',
+      })
+    );
+    expect(deps.log).toHaveBeenCalledWith('Schedule created: sched-2');
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/commands/cloud.test.ts` around lines 118 - 171, Add a new test case
alongside the existing schedule test to cover one-time scheduling via the --at
flag: mock cloudMocks.scheduleWorkflow.mockResolvedValueOnce to return a
one-time schedule (e.g., id 'sched-at-1', scheduleType 'at' or 'one-time',
atTimestamp set, lastTriggeredRunId null), call program.parseAsync with the same
args but replace '--cron' and cron expression with '--at' and an ISO timestamp,
then assert cloudMocks.scheduleWorkflow was called with 'workflow.yaml' and an
object containing the at/timestamp field (and no cron), and assert deps.log was
called with the created schedule id string (e.g., 'sched-at-1') to verify the
output.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.trajectories/completed/2026-05/traj_u4ixmbqqm2y1.json:
- Line 19: The JSON key "projectId" contains an absolute local path that leaks a
user-specific filesystem location; replace the value of "projectId" (the entry
named projectId) with a stable, non-sensitive identifier such as the repository
name, a relative path, or a UUID (e.g., "relay" or "repo:relay") instead of
"/Users/khaliqgant/Projects/AgentWorkforce/relay" to avoid embedding local
environment details.

In @.trajectories/index.json:
- Line 333: The index entry currently contains a machine-specific absolute path
string
"/Users/khaliqgant/Projects/AgentWorkforce/relay/.trajectories/completed/2026-05/traj_u4ixmbqqm2y1.json";
update that JSON value to the repo-relative path
".trajectories/completed/2026-05/traj_u4ixmbqqm2y1.json" so the "path" field
matches the rest of the index and is not machine-specific.

In `@packages/cloud/src/workflows.ts`:
- Around line 927-937: The type guard isWorkflowSchedule currently permits
schedules that lack fields callers expect (e.g., timezone and cadence
timestamps); update it to validate those fields: keep the existing
id/name/status/scheduleType checks, and additionally require record.timezone to
be a string when record.scheduleType === 'cron', and require cadence timestamp
fields (e.g., record.startTime and record.endTime) to be numbers (or valid
timestamp types used elsewhere) when those fields are present or required by
your callers; update the predicate inside isWorkflowSchedule (operating on the
record variable) to include these checks so malformed schedule objects are
rejected.

---

Nitpick comments:
In `@packages/cloud/src/workflows.test.ts`:
- Around line 430-528: Add tests covering the one-time schedule path and invalid
option combinations for scheduleWorkflow/listWorkflowSchedules: create a new
spec that mirrors the existing "creates a cron schedule..." test but calls
scheduleWorkflow with the at option (e.g., at: '2026-01-01T00:00:00Z') and
assert the authorizedApiFetchMock request body indicates a one-time
schedule_type (or equivalent) and that workflowRequest includes/omits
runId/s3CodeKey as appropriate; then add two negative tests that call
scheduleWorkflow with both cron and at and with neither cron nor at and assert
they throw or reject (use the same test harness and authorizedApiFetchMock
patterns used in the file). Reference scheduleWorkflow and listWorkflowSchedules
and reuse authorizedApiFetchMock/scheduleBodies setup and assertions style from
the existing cron test.

In `@src/cli/commands/cloud.test.ts`:
- Around line 118-171: Add a new test case alongside the existing schedule test
to cover one-time scheduling via the --at flag: mock
cloudMocks.scheduleWorkflow.mockResolvedValueOnce to return a one-time schedule
(e.g., id 'sched-at-1', scheduleType 'at' or 'one-time', atTimestamp set,
lastTriggeredRunId null), call program.parseAsync with the same args but replace
'--cron' and cron expression with '--at' and an ISO timestamp, then assert
cloudMocks.scheduleWorkflow was called with 'workflow.yaml' and an object
containing the at/timestamp field (and no cron), and assert deps.log was called
with the created schedule id string (e.g., 'sched-at-1') to verify the output.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b5f1ba72-501f-443e-a6f8-d3bb314c67b2

📥 Commits

Reviewing files that changed from the base of the PR and between 272cc13 and 729a7ec.

📒 Files selected for processing (9)
  • .trajectories/completed/2026-05/traj_u4ixmbqqm2y1.json
  • .trajectories/completed/2026-05/traj_u4ixmbqqm2y1.md
  • .trajectories/index.json
  • packages/cloud/src/index.ts
  • packages/cloud/src/types.ts
  • packages/cloud/src/workflows.test.ts
  • packages/cloud/src/workflows.ts
  • src/cli/commands/cloud.test.ts
  • src/cli/commands/cloud.ts

Comment thread .trajectories/completed/2026-05/traj_u4ixmbqqm2y1.json Outdated
Comment thread .trajectories/index.json Outdated
Comment thread packages/cloud/src/workflows.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/cloud/src/workflows.test.ts (1)

501-516: ⚡ Quick win

Add a cron-mode assertion that scheduled_at is omitted.

The one-time test already asserts cron_expression is absent; adding the mirror check in cron mode will better lock the request contract.

🔍 Suggested test addition
     expect(scheduleBodies[0]).toMatchObject({
       name: 'Hourly eval',
       schedule_type: 'cron',
       cron_expression: '0 * * * *',
       timezone: 'UTC',
       workflowRequest: {
         fileType: 'yaml',
       },
     });
+    expect((scheduleBodies[0] as { scheduled_at?: unknown }).scheduled_at).toBeUndefined();
     expect(
       (scheduleBodies[0] as { workflowRequest: Record<string, unknown> }).workflowRequest.runId
     ).toBeUndefined();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cloud/src/workflows.test.ts` around lines 501 - 516, The cron-mode
test is missing an assertion that scheduled_at is omitted from the request;
update the existing cron assertions for scheduleBodies[0] to also assert that
(scheduleBodies[0] as { workflowRequest: Record<string, unknown>
}).workflowRequest.scheduled_at is undefined so the cron request mirrors the
one-time contract (you already check cron_expression presence — add scheduled_at
absence). Use the existing scheduleBodies and workflowRequest references to
locate where to add this expect.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/cloud/src/workflows.test.ts`:
- Around line 501-516: The cron-mode test is missing an assertion that
scheduled_at is omitted from the request; update the existing cron assertions
for scheduleBodies[0] to also assert that (scheduleBodies[0] as {
workflowRequest: Record<string, unknown> }).workflowRequest.scheduled_at is
undefined so the cron request mirrors the one-time contract (you already check
cron_expression presence — add scheduled_at absence). Use the existing
scheduleBodies and workflowRequest references to locate where to add this
expect.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1654110b-9339-4858-a19c-c64e663b2661

📥 Commits

Reviewing files that changed from the base of the PR and between 729a7ec and d72030f.

📒 Files selected for processing (12)
  • .trajectories/completed/2026-05/traj_oyc528j7suvo.json
  • .trajectories/completed/2026-05/traj_oyc528j7suvo.md
  • .trajectories/completed/2026-05/traj_u4ixmbqqm2y1.json
  • .trajectories/completed/2026-05/traj_uf8y40ewrfh0.json
  • .trajectories/completed/2026-05/traj_uf8y40ewrfh0.md
  • .trajectories/index.json
  • packages/cloud/src/workflows.test.ts
  • packages/cloud/src/workflows.ts
  • packages/sdk/package.json
  • packages/sdk/src/workflows/cloud-schedules.ts
  • packages/sdk/src/workflows/index.ts
  • src/cli/commands/cloud.test.ts
✅ Files skipped from review due to trivial changes (9)
  • packages/sdk/src/workflows/index.ts
  • .trajectories/completed/2026-05/traj_uf8y40ewrfh0.md
  • packages/sdk/src/workflows/cloud-schedules.ts
  • packages/sdk/package.json
  • .trajectories/completed/2026-05/traj_uf8y40ewrfh0.json
  • .trajectories/completed/2026-05/traj_u4ixmbqqm2y1.json
  • .trajectories/completed/2026-05/traj_oyc528j7suvo.md
  • .trajectories/completed/2026-05/traj_oyc528j7suvo.json
  • .trajectories/index.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/cli/commands/cloud.test.ts
  • packages/cloud/src/workflows.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Line 47: The changelog entry contains a duplicated PR reference: the line
"Reclaim agent on 409 instead of crashing the broker (`#797`) (`#830`) (`#797`)"
should list each PR only once; edit CHANGELOG.md to remove the duplicate
"(`#797`)" so the line becomes "Reclaim agent on 409 instead of crashing the
broker (`#797`) (`#830`)" (or reorder if you prefer a different ordering), ensuring
no duplicate PR IDs remain.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d8337581-0844-420c-b300-023df2ed78be

📥 Commits

Reviewing files that changed from the base of the PR and between d72030f and 02ee37c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • .trajectories/completed/2026-05/traj_iole5zdt9orr.json
  • .trajectories/completed/2026-05/traj_iole5zdt9orr.md
  • .trajectories/index.json
  • CHANGELOG.md
  • packages/sdk/package.json
  • packages/sdk/tsconfig.build.json
  • packages/sdk/tsconfig.json
  • packages/telemetry/src/events.ts
  • scripts/build-cjs.mjs
  • src/cli/bootstrap.test.ts
✅ Files skipped from review due to trivial changes (4)
  • packages/sdk/tsconfig.build.json
  • .trajectories/completed/2026-05/traj_iole5zdt9orr.json
  • src/cli/bootstrap.test.ts
  • .trajectories/index.json

Comment thread CHANGELOG.md

#### User-Impacting Fixes

- Reclaim agent on 409 instead of crashing the broker (#797) (#830) (#797)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove duplicated PR reference in changelog entry.

Line 47 repeats (#797) twice, which creates noisy release traceability. Keep each reference once.

Proposed fix
-- Reclaim agent on 409 instead of crashing the broker (`#797`) (`#830`) (`#797`)
+- Reclaim agent on 409 instead of crashing the broker (`#797`) (`#830`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Reclaim agent on 409 instead of crashing the broker (#797) (#830) (#797)
- Reclaim agent on 409 instead of crashing the broker (`#797`) (`#830`)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` at line 47, The changelog entry contains a duplicated PR
reference: the line "Reclaim agent on 409 instead of crashing the broker (`#797`)
(`#830`) (`#797`)" should list each PR only once; edit CHANGELOG.md to remove the
duplicate "(`#797`)" so the line becomes "Reclaim agent on 409 instead of crashing
the broker (`#797`) (`#830`)" (or reorder if you prefer a different ordering),
ensuring no duplicate PR IDs remain.

@khaliqgant khaliqgant merged commit bffd6b2 into main May 10, 2026
53 checks passed
@khaliqgant khaliqgant deleted the codex/cloud-workflow-schedule-cli branch May 10, 2026 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant