Back to Build in Public
Build in public14 min read

The Heartbeat Had to Know When to Stay Quiet

A recurring task is easy to describe as a timer. Building one we could trust meant designing the pauses, the limits, the reports, and the moments where the agent must wait for a human.

By Open Free Max

The Heartbeat Had to Know When to Stay Quiet

The first version of the idea sounded almost too simple.

Give an agent a checklist. Give it a schedule. Let it check the project while we are doing something else.

That sentence contains a product feature, but it also contains a trap. The moment software is allowed to act without a person watching every turn, the important question stops being whether it can start a session. The important question becomes whether it knows when not to create more work.

We called the feature heartbeat because it needed to be regular without becoming noisy. A heartbeat is a signal that something is alive. It is not an alarm that demands attention every few minutes. That distinction became the centre of the work.

The request behind the request

The users who helped shape this feature did not usually ask for an autonomous agent. They asked for smaller things.

« Can it check the project every morning? »

« Can it look for dependency issues while I am away? »

« Can it review the notes and tell me if something needs attention? »

« Can it keep doing the boring part without turning every quiet check into a notification? »

Those are ordinary requests. They are also more precise than the word autonomy suggests. People were not asking Open Free Max to take over their projects. They were asking it to carry a repeatable responsibility between the moments when they sat down at the keyboard.

That changed the product shape. A general autonomous mode would have been a large promise with a vague boundary. A recurring job is a smaller promise: at a defined time, in a defined project, an official CLI agent receives a defined checklist, works through the existing local pipeline, and leaves a defined result.

The boundary mattered. The job had to be understandable before it ran.

A timer is not a workflow

Our earliest mental model was a timer that launched a command. That model is familiar because it is how many automation systems begin. It is also incomplete for work involving an agent.

A timer knows when to wake up. It does not know whether the engine is paused, whether another turn from the same job is still running, whether there is a free worker slot, whether the daily limit has been reached, whether the checklist is empty, or whether the machine has been asleep.

An agent turn has a lifecycle. It needs a project, a provider, a target environment, a prompt template, an output convention, and a place where the result can be inspected. It needs a way to distinguish « nothing changed » from « something was checked and a report was produced ». It needs a way to stop when the answer requires a judgement that the system cannot safely make.

So the heartbeat became a source of work for the orchestrator, not a separate automation engine. It creates a small, temporary campaign at the moment a schedule is due. That campaign enters the same worker, PTY, and completion pipeline as the rest of Open Free Max. The feature has its own scheduling decisions, but it does not create a parallel universe for executing agent sessions.

That reuse was one of the first decisions we kept.

Why the quiet result mattered so much

An agent checking a project will often find nothing that needs action. That is not a failure. It is the expected result of a healthy recurring check.

If every successful check created a report, the user would quickly learn to ignore the reports. If every check created a notification, the notification channel would become a second inbox. The feature would technically work while becoming practically useless.

We therefore made the acknowledgement a first-class result. The agent can finish a turn by writing an acknowledgement that means « nothing to do ». The status updates. The run is recorded. No report is added to the reading list.

When there is a meaningful change, the turn writes a report that the user can open. The difference is simple to explain and surprisingly important to feel. Silence becomes informative because it means the scheduled responsibility was checked and did not require escalation.

This was also a useful test of our product instincts. We tend to measure visible output because visible output is easy to demo. A recurring feature is better when it produces less visible output most of the time. We had to accept that the best screenshot might be a clean status line and no notification.

The schedule had to be readable

There are two useful ways to express a recurrence in the first version: an interval, such as every six hours, or fixed local times, such as 08:00 and 18:00. We deliberately did not begin with a full cron expression language.

Cron is powerful. It is also a compact programming language that makes a schedule difficult to read at a glance. A recurring job in Open Free Max is meant to be configured in the same place where a person works with projects and agents. The schedule should communicate its intent without requiring a reference page.

The same reasoning shaped the active-hours window. A job can run at any time by default, or it can be limited to a window in the machine's local time. There are no named timezone abstractions hiding behind the control. The heartbeat runs where the local project and the local CLI run, so the machine's own clock is the honest reference.

That honesty has a consequence: the heartbeat is 24/7 in the sense that the default has no night window, not in the sense that a sleeping machine can be contacted from elsewhere. If the computer sleeps, no turn fires. Open Free Max does not wake a closed laptop. A keep-awake option can prevent idle sleep while an active job requests it, but it cannot defeat the operating system's behaviour when the lid is closed.

We wanted that limitation in the product story, not hidden in a footnote.

Missed work is not owed work

The next difficult decision was what to do after a machine comes back from sleep.

Imagine a job scheduled every thirty minutes. The computer is off for three days. A literal catch-up strategy would see 144 missed slots and attempt to replay them. That might sound reliable, but it would be a poor default. It could consume the user's quota in a burst, create reports about stale conditions, and make the first action after reboot surprising and expensive.

The heartbeat does not replay missed slots. At boot, it reschedules from the current time and records that slots were missed. The next legitimate slot is the one that runs.

This is not a claim that missed work never matters. Some workflows genuinely need durable event processing. That is a different product problem. Recurring work is a periodic observation, not a ledger of obligations. When the machine was unavailable, the observation could not happen. Replaying every observation after the fact would pretend otherwise.

The rule is easy to state: a scheduled check is not a debt.

Human work must keep its place

The heartbeat runs alongside sessions that a person opens by hand. That « alongside » is a requirement, not marketing language.

An automated check should never make a user wait to start a manual session. This is especially important for an IDE built around official command-line agents. The person at the keyboard is already the highest-value source of context. If they need to open a project and ask a question, that session must begin immediately even when recurring work is active.

The orchestrator therefore treats heartbeat workers as a separate class from manual sessions. The configured concurrency limit applies to the orchestrator's workers, while a hand-opened session retains its direct path. Within the recurring pool, priority and the optional « reserve the last slot » setting help keep the balance predictable.

This is a small architectural detail with a large emotional effect. A person should not feel that they have to negotiate with their own automation before they can work.

The point where automation has to stop

The most important heartbeat behaviour is not what happens when a task is clear. It is what happens when the task is unclear.

An agent can inspect a dependency update and report it. It can find a stale note and suggest a change. It can run a repeatable review when the checklist gives it enough context. But some decisions are genuinely undecidable from the files in front of it. A production change might have two reasonable options. A broad refactor might affect a convention that is not written down. A destructive action might be technically possible but outside the user's intent.

We did not want the heartbeat to turn uncertainty into confidence. When a turn needs approval, it is sent to the human approval queue and marked as coming from a recurring job. The system notification is the exception to the quiet default because the system has reached a boundary that deserves a person.

That boundary also exposed a provider-specific problem. Official CLIs have different approval flags and different interactive behaviours. A careful interactive configuration is often the right setting for a human session, but an unattended turn cannot make progress if it stops at the first confirmation. The schedule can carry its own provider settings override, limited to that job, so the user does not have to weaken the configuration of every manual session.

We also explicitly refuse flags that turn a recurring job into a headless or programmatic API path. The heartbeat drives the official CLI interactively through a local PTY. It is not an SDK runner, a hidden `-p` shortcut, or a way around a provider's subscription model.

This was a line we wanted the implementation to enforce, not merely the documentation to recommend.

Quota is a user decision

Recurring work consumes the same subscription quota as a normal session. That fact is easy to bury behind the excitement of a schedule, so we kept pulling it back into the design.

The global settings provide conservative defaults: a daily turn count, a minimum interval, a default priority, a checklist size limit, and a retention count for failed turns. Each schedule can inherit those settings, override its daily turn count, or set zero when the user intentionally wants no high-level cap. The feature does not invent a hidden billing meter, and it does not pretend that « automatic » means « free ».

The limits are there to make consumption visible and controllable. Each job shows its turns for the day. The usage view gives the broader picture. A checklist that is empty does not launch a turn, and a skipped check does not quietly consume a quota slot merely because the clock reached its time.

We were careful with the language around these controls. A limit is not a claim about what the product believes the user should consume. It is a handle the user can set. Open Free Max can provide safe defaults and clear feedback. The level of recurring work remains the user's decision.

The output belongs to the project

A recurring job needs a result that survives the end of the session. We chose a deliberately plain convention: reports and acknowledgements live under the project's `.ofm/heartbeat` output directory by default.

The convention gives the agent a concrete place to write and the user a concrete place to inspect. Reports are links in the interface. Acknowledgements remain quiet artifacts that prove the turn completed without filling the page with repeated prose.

The design also supports projects that are not code repositories. A book, a research folder, a collection of notes, or a vault can have recurring work. Nothing in the feature assumes a build command, a test suite, or a Git branch. When a project has Git, the generated guidance can remind the CLI to stay on the current branch. When it does not, Open Free Max does not invent a branch boundary that cannot exist.

That distinction is important because automation has different risks in different project types. A code repository gives you version history. A notes folder might not. The heartbeat can state the boundary, but it cannot create a backup strategy on the user's behalf. For a first job, a read-only report is a safer way to learn how the feature behaves.

Memory made the schedule more useful

The heartbeat also forced us to think about continuity. A scheduled turn that starts from zero every time can inspect the current files, but it cannot easily remember why a previous turn chose a particular path.

Open Free Max can ask a job to recall the project's local memory before acting. It can also feed a completed report into that project memory deterministically. An acknowledgement has nothing to add, so it is not ingested. The combination is intentionally explicit: the schedule decides whether it should recall and whether its reports should feed memory.

There is a separate switch for keeping a single note in an Obsidian vault. That operation happens through the vault's MCP server and therefore depends on the agent following the instruction. It does not silently turn every recurring run into a new note. One job, one durable note, updated as the work evolves, is a much better fit than 24 nearly identical notes per day.

These controls keep memory from becoming a magical side effect. The user can see which kind of continuity a job has been asked to maintain.

What we learned by making it real

The first lesson was that autonomy is mostly about boundaries. The code that fires a due schedule is not the whole feature. The product becomes trustworthy through the rules around that fire: empty checklists do nothing, missed slots are not replayed, human sessions are protected, ambiguous decisions wait, and provider flags remain scoped.

The second lesson was that quiet is a user interface state. Acknowledgement is not a missing feature or an empty report. It is the answer to a recurring question when the answer is « nothing needs your attention ». Designing that state made the heartbeat feel less like a notification machine and more like a responsible assistant.

The third lesson was that local software still needs to explain its limits. A local heartbeat cannot run while the machine is asleep. It cannot safely infer every business decision. It cannot create a backup for an unversioned folder. It cannot make an agent wiser than the context and instructions it receives. Those are not embarrassing caveats. They are the conditions that let a user decide whether a job fits their project.

The fourth lesson was that the existing pipeline was worth protecting. By making the heartbeat produce temporary work that travels through the established campaign, worker, PTY, and completion path, we kept the new behaviour visible to the same audit and approval surfaces. The feature gained leverage from the system that already existed, and the rest of the system did not have to learn a second way to run an agent.

There was a fifth lesson in the small details. A schedule needs a visible next run, a visible last decision, and a visible reason when it is waiting. « Deferred » is not the same as « failed ». « Skipped because the checklist is empty » is not the same as « skipped because the daily limit was reached ». If the interface compresses all of those states into a single grey status, the user is forced to reconstruct the scheduler's reasoning. We kept separating them because a recurring system earns trust one explanation at a time.

That also changed how we thought about testing. It was not enough to test that an agent eventually launched. We needed to test the decisions before launch: a paused engine must stay paused, a running turn must not overlap with itself, a reserved slot must remain reserved, and a missed boot window must not become a surprise burst. The heartbeat is a small feature on screen, but its reliability lives in the decisions that prevent a turn from starting.

The feature we wanted to keep using

The most useful test of a recurring-work feature is not whether it looks impressive in a demo. It is whether the team keeps a job turned on after the first week.

That usually means the job has a narrow checklist. It has a cadence that matches the actual work. It writes a report only when the report carries new information. It stops for decisions that belong to a human. It leaves manual sessions alone. It has a limit that the owner understands.

In that shape, heartbeat work is not an attempt to replace attention. It is a way to reserve attention for the moments when attention is needed.

We are still learning where that boundary should move. The product will teach us through real schedules, real projects, and the cases where a user says that a job was too quiet or too eager. The design will evolve, but the principle is stable: Open Free Max should make useful work repeatable without making the user responsible for filtering a new stream of noise.

The heartbeat is alive when the project needs a check. It is successful when the rest of the day can continue without interruption.

TO KEEP IN MIND

  • A recurring job is a checklist, a schedule, and a defined output, not a blank promise of autonomy.
  • An acknowledgement means the check completed and found nothing that needed attention.
  • Missed slots are not replayed after sleep or shutdown.
  • Manual sessions stay available even while recurring workers are running.
  • Ambiguous or approval-sensitive work is sent back to a human.

THE PRACTICAL ADVICE

Start with a read-only checklist that produces a report only when it finds a concrete change. Give it a wide enough interval to match the pace of the project, set a visible daily limit, and run it beside your normal interactive workflow for a week before allowing it to write anything.

Keep exploring

More from Build in Public

Browse the publication