Skip to content

Refactor heartbeat commands onto control plane#162

Merged
roackb2 merged 2 commits into
mainfrom
codex/heartbeat-command-boundary
Jun 3, 2026
Merged

Refactor heartbeat commands onto control plane#162
roackb2 merged 2 commits into
mainfrom
codex/heartbeat-command-boundary

Conversation

@roackb2
Copy link
Copy Markdown
Owner

@roackb2 roackb2 commented Jun 3, 2026

Summary

  • Move heartbeat terminal command ownership into src/cli-v2/commands, leaving src/cli/heartbeat.ts as a legacy compatibility export only.
  • Add shared command-edge control-plane runtime bootstrap so heartbeat can attach to a live server or start an embedded server before using the API.
  • Route heartbeat task/runs/run/start behavior through control-plane heartbeat API procedures instead of constructing task objects or running a CLI-local scheduler loop.
  • Add heartbeatRunDueTasks control-plane mutation so heddle heartbeat run can ask the server to execute due work.
  • Document the public heartbeat boundary: command adapters may use the control-plane API or documented service methods, but should not own task mutation or recurring execution policy.

Expected Behavior

  • heddle heartbeat task ... and heddle heartbeat runs ... now attach to the live control-plane server when one exists; otherwise they start an embedded control-plane server for the command.
  • heddle heartbeat run asks the server to run due heartbeat tasks, or one explicit task with --task.
  • heddle heartbeat start creates or updates a heartbeat task through the API and reports the server-backed scheduler. If it started an embedded server and is not running --once, it keeps that server alive until Ctrl+C.
  • Web Tasks/heartbeat views and terminal heartbeat commands now read/write the same control-plane task/run shapes.

Verification

  • yarn typecheck
  • yarn lint
  • yarn test:unit src/__tests__/unit/tui/heartbeat-cli.test.ts src/__tests__/unit/cli-v2/chat-v2-runtime.test.ts src/__tests__/unit/core/import-boundaries.test.ts
  • yarn test:integration src/__tests__/integration/control-plane/control-plane-heartbeat-mutations.test.ts
  • git diff --check
  • Smoke:
    • yarn -s cli:dev heartbeat task list
    • yarn -s cli:dev heartbeat runs list --limit 1

Manual Test Notes

  • With a live Heddle server: run yarn -s cli:dev heartbeat task list; expected output starts with an attach notice and lists tasks from the control-plane API.
  • With a live Heddle server: run yarn -s cli:dev heartbeat runs list --limit 1; expected output starts with an attach notice and prints the latest run view.
  • Without a live server: run yarn -s cli:dev heartbeat task list; expected output starts with an embedded server notice, then returns the API-backed task list.
  • For a safe no-due-work workspace, run yarn -s cli:dev heartbeat run; expected output is checked=... ran=... failed=... from the server-backed due-task mutation.
  • If intentionally triggering a task, run yarn -s cli:dev heartbeat run --task <taskId>; expected output is accepted=true task=<taskId> status=<status>.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aafa7158d2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli-v2/commands/heartbeat-command.ts
Comment thread src/cli-v2/commands/heartbeat/worker.ts Outdated
) {
const id = stringFlag(parsed.flags, 'id') ?? parsed.subcommand ?? DEFAULT_HEARTBEAT_TASK_ID;
const intervalMs = parseDurationMs(stringFlag(parsed.flags, 'every') ?? stringFlag(parsed.flags, 'interval') ?? '30m');
const pollIntervalMs = parseDurationMs(stringFlag(parsed.flags, 'poll') ?? '60s');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not report an unapplied poll interval

For heddle heartbeat start --poll <duration>, this parses and later prints the requested poll interval, but the embedded control-plane server and scheduler were already started before this function is called and no pollIntervalMs is passed through ControlPlaneCommandRuntimeService.resolve/startHeddleControlPlaneServer. In embedded mode the scheduler still uses the server default poll interval, so the command can claim poll=5s while actually polling at the default cadence.

Useful? React with 👍 / 👎.

@roackb2 roackb2 force-pushed the codex/heartbeat-command-boundary branch from aafa715 to 10d78d2 Compare June 3, 2026 10:17
@roackb2 roackb2 force-pushed the codex/heartbeat-command-boundary branch from 10d78d2 to 2a48ad6 Compare June 3, 2026 11:46
@roackb2 roackb2 merged commit 43fc81c into main Jun 3, 2026
5 checks passed
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