Skip to content

Heartbeat: do not wake the orchestrator on an idle floor - #30

Merged
aaroncoville merged 2 commits into
theme/sixth-historyfrom
feat/heartbeat-idle-suppress
Sep 28, 2026
Merged

aaroncoville merged 2 commits into
theme/sixth-historyfrom
feat/heartbeat-idle-suppress

Conversation

@aaroncoville

Copy link
Copy Markdown
Owner

Stops the floor heartbeat from waking the orchestrator when there is nothing to review.

Today a beat re-engages the orchestrator whenever the floor has been quiet for quietThresholdMs, even with no unread mail and no work in flight. Measured over one idle morning that was 23 consecutive no-op wakes, each re-reading a large context for no action.

Decision extracted. src/main/heartbeatPolicy.ts holds the pure shouldReengage() rule (Electron-free, like breaker.ts and workerWake.ts): actionable inbox mail always re-engages; an active floor never does; a quiet floor re-engages unless suppressWhenIdle is on and tasks.json has no card in doing. A quiet floor with work in flight may be a stall and still wakes the orchestrator.

Fail toward waking. doingTaskCount() treats a missing root, missing file, corrupt JSON or a non-array tasks field as "at least one doing card". Only a present, valid, empty array is a trusted zero. The ledger is read only when it can change the outcome.

Reversible without a rebuild. New ScheduledMission.suppressWhenIdle (default true, baked onto the heartbeat mission); false restores the previous behaviour. A suppressed beat appends a heartbeat-suppressed event to log.jsonl and re-arms on the normal cadence, with no back-off.

Verification: test/heartbeat-policy.test.cjs, 11 cases written red-first, covering all four decision directions and the fail-open contract; typecheck clean; full suite 1450 pass with the failing set identical by name to the base commit (sandbox-bound tests). Takes effect on the next app restart.

Aaron Coville added 2 commits September 28, 2026 10:56
beat() lived entirely inside index.ts, which imports Electron and
cannot be exercised outside the main process. Pull the actual
decision — and the tasks.json 'doing' count it will soon depend on —
into heartbeatPolicy.ts (node builtins only) so both can be unit
tested directly, following the same pattern already used for the
breaker and worker-wake watchdog.

doingTaskCount() is deliberately fail-open toward "has doing cards"
on any read failure (missing file, corrupt JSON, wrong shape) rather
than toward zero, since it will gate whether a quiet floor is left
alone.
Until now every beat re-engaged god on any quiet floor, regardless of
whether there was work in flight — a floor that finished its work
hours ago gets re-engaged on the same cadence as one mid-stall. Gate
that case on tasks.json: a quiet floor with at least one 'doing' card
may be a stall worth a look; a quiet floor with nothing doing has
nothing for god to review.

Real inbox mail still always re-engages regardless of this gate.

Reversible without a rebuild via the new suppressWhenIdle flag on the
heartbeat mission (config.json), default true. A suppressed beat logs
a heartbeat-suppressed event (reason + counts) instead of silently
doing nothing, and re-arms on the normal cadence rather than the
back-off a real re-engage takes.
@aaroncoville
aaroncoville merged commit 75696f7 into theme/sixth-history Sep 28, 2026
@github-actions

Copy link
Copy Markdown

🚫 This PR is missing its before/after evidence

Every pull request here has to show its work. Screenshots or a short screen recording, before the change and after it.

  • Before — no image or video under that heading
  • After — no image or video under that heading

How to fix it: edit the description, keep the ### Before and ### After headings from the template, and drag an image or video under each. GitHub uploads it inline. This check re-runs the moment you save.

A bug fix with no visible surface still needs it: show the failing behaviour, then the same steps passing. A terminal recording is fine.

Genuinely nothing to show — a CI tweak, a typo, a dependency bump? A maintainer can apply the no-visual-change label. Please don't ask unless it truly has no observable effect.

📖 CONTRIBUTING.md → Evidence is mandatory

aaroncoville pushed a commit that referenced this pull request Sep 28, 2026
No conflicts. index.ts carries both the codex hook-trust wiring and the
extracted heartbeat re-engage policy.
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