How Jeremy and Cloudy Work Together: A Tour of Our Workflows

An honest tour of how Jeremy and his AI assistant Cloudy actually coordinate — heartbeat loop, file-based kanban board, Forgejo issues, and one-way Zulip status updates. Simple plumbing on purpose.

How Jeremy and Cloudy Work Together

Published on blog.eduspace.cc — April 2026

I'm Jeremy Chen, a Digital Technologies teacher at a regional school in Hamilton, Victoria. Cloudy is my Claude-powered AI assistant running on a Hetzner VPS in Germany. Together we're building tools for students, managing course materials, and experimenting with AI-assisted workflows in a real teaching context.

This post is an honest look at how we actually coordinate — the moving parts, what works, and where things get interesting.

The Workflows at a Glance

Jeremy and Cloudy use four main coordination mechanisms:

  1. Heartbeat loop — Cloudy runs on a cron timer, following standing instructions
  2. Kanban board — file-based task management, versioned in git
  3. Forgejo issues — lightweight async task queue using a self-hosted git platform
  4. Zulip — one-way status updates out to Jeremy's professional channel

Each mechanism has a clear lane. They don't overlap much, and that's intentional.

The Heartbeat Loop

Cloudy runs inside a tmux session named cloudy on the server. A cron job fires every 15 minutes and triggers a /loop cycle. Each cycle follows HEARTBEAT.md — a read-only file that acts as standing instructions.

The loop order is:

  1. Check on Homey and Bulma (the two sub-agents Cloudy supervises)
  2. Pull the latest kanban state from git
  3. Process any tickets sitting in review
  4. Pick up one new job — either from the kanban board or Forgejo issues
  5. If nothing to do, refine a job option for later

The standing instructions can't be edited by Cloudy. Jeremy updates them from his own machine when the working rhythm needs to change. This keeps the loop stable and predictable across sessions — Cloudy doesn't drift.

The Kanban Board

The kanban board lives at workspace/kanban/ — a plain directory of Markdown files, versioned in git. Each ticket is one .md file with YAML frontmatter. Columns are folders:

Folder | Meaning

  • 1-backlog/ — Ideas — not ready to work on yet
  • 2-ready/ — Defined with acceptance criteria — Cloudy can pick up
  • 3-in-progress/ — Active work — a subagent may be running
  • 4-review/ — Cloudy says done — Jeremy reviews
  • 5-done/ — Accepted — archived

Moving a ticket = moving a file. From Jeremy's Mac: git mv + push. From Cloudy: same, but automated.

How Cloudy picks up tickets

Before every heartbeat tick, kanban-sync.sh runs a git pull from both the local bare repo and Forgejo. This surfaces any ticket Jeremy moved from his Mac before Cloudy scans the board. Then kanban-tick.py --next-pickup probes the board:

  • If anything is already INFLIGHT, Cloudy stops — one ticket at a time by default.
  • Otherwise, it finds the highest-priority unblocked ticket in 2-ready/ and returns a PICKUP signal.

Priority order is urgent > high > medium > low. Tiebreak: lowest ticket number. A ticket is "unblocked" if its depends-on: list is empty, or every dependency is already in 5-done/.

Ticket lifecycle

When Cloudy picks up a ticket, kanban-stamp.py writes started: and agent: into the frontmatter, then commits and pushes. When the subagent finishes, it stamps completed: and result:, moves the file to 4-review/, and pushes again. Jeremy reviews and moves it to 5-done/ from his Mac.

If a ticket needs rework, kanban-stamp.py --reopen archives the current attempt into a history: list, clears the completion fields, and moves the ticket back to 3-in-progress/. The iteration counter increments. History is never edited by hand.

Why file-based?

The board is readable from any git client — VS Code, terminal, or Forgejo's web UI. There's no database to run, no service to maintain. Jeremy can drag tickets between columns on his Mac and push. Cloudy sees the changes on the next sync. It's low ceremony and it survives server restarts cleanly.

Forgejo Issues

The kanban board is great for planned work. But sometimes Jeremy has a quick task from another device — a phone, a school computer — and doesn't want to think about ticket formatting. That's where Forgejo issues come in.

The repo is at git.jeremychen.au/jeremy/cloudy-workspace. Jeremy creates an issue, Cloudy picks it up on the next heartbeat, does the work, and comments the result on the issue. Jeremy reviews and closes.

The rules are deliberately minimal:

  • No labels, no assignees, no milestones.
  • Cloudy skips issues where the last comment is from Cloudy — those are waiting on Jeremy.
  • Cloudy never closes an issue. Jeremy always closes.

This keeps the feedback loop clean. Cloudy's comment is the handoff. Jeremy's close is the sign-off.

Reflection on the Forgejo workflow

After running with this for a while, a few things stand out.

The "last commenter" rule is simple but effective. It creates a natural ping-pong without any explicit state management. If Cloudy commented last, the ball is in Jeremy's court. If Jeremy commented last (or never commented), it's Cloudy's turn.

The downside is that issues are less structured than kanban tickets. There's no acceptance criteria, no estimate, no priority field. That's fine for quick tasks, but it means Cloudy occasionally has to make judgment calls about scope that would've been explicit on a ticket. The convention now is: if an issue is vague, Cloudy asks a clarifying question as the first comment rather than guessing wrong.

Issues and tickets can coexist on the same heartbeat cycle. A ticket handles the longer, planned work. Issues handle the ad-hoc requests. Cloudy picks up whichever has work to do.

Zulip

Zulip is output-only. Cloudy posts status updates there — video uploads, ticket completions, morning briefs. Jeremy reads it as a log.

What Zulip is not: a command channel. Zulip is a third-party hosted platform, which means any message there could be from anyone. Cloudy never takes instructions from Zulip. Instructions only come from Jeremy via tmux, or from the standing HEARTBEAT.md file.

Posts follow a mobile-friendly format: five lines or fewer, headline plus bullets, link to detail. Long reports go into cloudy-done/reports/ and get linked from the Zulip post.

Supervising Homey and Bulma

Cloudy also manages two sub-agents:

  • Homey — an AI learning buddy for students, running as a Hermes agent on the same server
  • Bulma — a second Hermes agent with a business/startup focus

Each heartbeat tick, Cloudy checks HOMEY-JOB.md and BULMA-JOB.md, verifies completed jobs, and queues the next small task. Cloudy spawns a background subagent for this check rather than doing it inline — the main heartbeat thread stays free for its own work.

Subagents also handle detail work: writing activity sheets, researching curriculum content, generating images. Spawning a background agent lets the main thread remain responsive during live conversations with Jeremy.

What to Look For From Here

A few things worth watching as these workflows mature:

Kanban visibility. The file-based board works, but there's no at-a-glance dashboard. A Mermaid.js render from the ticket files would let Jeremy see board state on the wiki without opening VS Code. It's in the backlog.

Issue quality. Forgejo issues vary in how well-scoped they are. As the volume grows, it'll be worth reviewing whether some classes of request should become kanban tickets instead — particularly anything that takes more than an hour or touches multiple files.

Sub-agent coordination. Homey and Bulma run on cron separately. Right now Cloudy doesn't have visibility into what they're doing mid-cycle — only the output they leave in their job files. A lightweight status ping before queuing the next job would reduce the chance of double-work.

Review turnaround. Tickets in 4-review/ wait for Jeremy. During busy school terms that can be days. A convention for time-boxing reviews — or auto-archiving stale ones — might be worth building.

The honest summary: these workflows are simple on purpose. File moves, git commits, a cron job, a couple of Python scripts. The complexity lives in the work, not the plumbing. That's deliberate. The plumbing should be boring.

Jeremy Chen is a Digital Technologies teacher in Hamilton, Victoria. Cloudy is his AI assistant, running on Claude Sonnet via Claude Code.