Skip to content

v4.5.3 - The memory-aware scheduler, single-card shipping and the settings sheet

Choose a tag to compare

@theyashgupta theyashgupta released this 14 Oct 14:10
1021106

On 2026-10-11, an 8 GB Mac froze with six loop sessions. The concurrency cap counts loops, but it does not count memory. An idle loop uses about 0.15 GB, and a review or QA loop uses about 1.5 GB. To recover, the operator killed three tmux sessions by hand, and the sessions lost the turns that were in progress.

Other gaps were in the orchestrator. It could start a single card, but it could not follow that card, ship it or prepare a release. Each single-card ship and each release was a decision for you. The orchestrator settings were only in the board side panel, so the Settings button of the command centre opened the board page. And the full check failed on a different tmux or timing test in each run while another workspace ran a suite.

This release fixes those problems. Each change is a follow-up to a released feature, so this is a patch release.

The orchestrator follows, ships and releases single cards (LOCAL-109)

A ticket card that the orchestrator started with start_card is now a loop card, the same as a group.

  • Progress. The new tool get_card_progress gives the stored progress and the last status marker of a launched card. The dashboard progress rows show a running launched card, with its phases when progress exists.
  • Events and the wake. The supervisor writes group_state events (agent_done, needs_input, start_failed, loop_error, usage_limit, shipped, ship_stopped) for each loop card. An idle orchestrator wakes for them. A single loop can report a loop_gate.
  • Handoff. request_handoff and the automatic handoff at the policy percent work for a single PRD loop. The resume file is resume.md in the feature folder.
  • Ship. start_ship accepts a launched ticket card in Agent done. It applies the same checks as a group. The session branch must exist, no flow can run, the branch must be ahead of its base and the worktree must be clean. A card with loop progress must have each phase passed, or the answer is loop-not-finished.
  • Release. The new tool release_prep reads package.json on the base branch from origin. It calculates the next version from the bump (patch, minor or major) and creates a Release v<next> card in To Do. A new seeded Release playbook makes the bump commit, the release notes and the demo recording. It publishes only after the bump PR merges, and only when the ship rights are merge.
  • Accounts. The new tool list_accounts gives each account with its state and its cached usage windows. It never gives an email, an organization, a token, a login time or a folder. start_card takes an optional account. An unknown account answers 400 unknown-account before any write.

Evidence: in scenario 6 part B, release_prep with bump minor on version 1.2.3 created the card Release v1.3.0, and a second call answered 409 release-exists. (PR #256)

The settings sheet replaces the board side panel (LOCAL-111)

The command centre is now the one orchestrator screen.

  • The sheet. Settings on the command centre opens a sheet at the right side with the tabs Policy and Orchestrators (n). The tabs have the same actions, dialogs and validation as the old panel.
  • Navigation. The URL holds the sheet state (settings=policy or settings=orchestrators). Back closes the sheet. Escape closes a dialog first, then the sheet. When the sheet closes, Settings gets the focus again.
  • No side panel. The board page shows only the board. An old link with panel=orchestrator opens the command centre. tab=policy or tab=orchestrators opens the sheet on that tab. Other tabs open the centre with the sheet closed.
  • Change budget. On the dashboard, Change budget on a budget stop opens the sheet on the Policy tab.
  • Start failures. When a background start fails, the centre shows "The orchestrator did not start: ." with Try again. Before this release, only the panel showed this line.

Evidence: /boards/<key>?panel=orchestrator&tab=policy opens the centre with the sheet on the Policy tab, and 70 of 70 visual cases pass. (PR #257)

The check runs tmux and timing tests alone, with one retry (LOCAL-117)

This change is for contributors.

  • Two steps. npm run test skips the 35 files in TIMING_FILES (scripts/run-tests.mjs). 22 of these files start a real tmux server, and 13 assert a tight time limit. The new npm run test:timing runs each of these files alone, in sequence, as the last step of npm run check.
  • One retry. A timing file fails the check only when it fails two times in sequence.
  • The check slot. npm run test and npm run test:timing both wait for a free check slot. Thus two workspaces cannot run the timing files at the same time.
  • Missing files. A renamed or deleted file in TIMING_FILES stops test:timing with exit code 2. Thus each test runs in one of the two steps.

Evidence: the local test:timing run gave "exit 0 in 621 s including the slot wait, 35 of 35 files passed on the first run, 0 retries". (LOCAL-117 ship notes)

The memory-aware loop scheduler (LOCAL-110 Units A to C)

Dispatch now reads the memory of the Mac and does not start or keep more loops than the memory can hold.

  • The reading. On macOS, the server reads free memory and swap every 15 s. GET /api/system/memory and the memory stream frame give the latest reading.
  • The start gate. Two new board policy fields set the limits: memoryFloorMb (default 1024) and swapCeilingMb (default 8192). Below the floor or above the ceiling, six start paths refuse a loop start with 409 memory-pressure. The reason names the reading and the limit. A reading older than 60 s, or no reading, lets the start continue.
  • Pause and unpause. POST /api/cards/:id/pause sends SIGSTOP to the pane process and to each descendant process. POST /api/cards/:id/unpause sends SIGCONT. A pause stops the process tree but keeps the turn that is in progress. A kill loses that turn. The supervisor shows a paused loop as paused, never as idle or stale. Each kill path sends SIGCONT first.
  • The governor. After each reading, swap above the ceiling pauses the newest loop that can pause, with the mark by: "memory". A second pause needs 256 MB more swap. The oldest loop that memory paused resumes after two readings below the ceiling minus 1024 MB. The governor never resumes a loop that you paused.
  • Orchestrator tools. The new MCP tools pause_card and resume_card use POST /api/orchestrator/sessions/:cardId/pause and /unpause. resume_card applies the memory gate first. The MCP server now has 35 tools.

Evidence: in scenario 8, swap at 9000 MB gave only the newest loop a by: "memory" mark. Swap at 9100 MB caused no second pause. Swap at 6000 MB resumed that loop, and the loop that the user paused stayed paused. (PR #259)

Memory pressure, Pause and Resume in the UI (LOCAL-110 Unit D)

  • The memory line. The Progress section of the dashboard shows "Memory: MB free, swap of MB". It adds ", paused" when loop cards of the board are paused, and "(stale)" when no reading came for 60 seconds. The line does not show on a computer that is not a Mac.
  • The Policy tab. Two number fields follow the concurrency cap. Free memory floor (MB) accepts 0 to 65536, and 0 turns the floor off. Swap ceiling (MB) accepts 256 to 65536.
  • Pause and Resume. Next to Reset, the card panel shows Pause for a loop card with a live session. It shows Resume for a paused card. A paused card tells you who paused it: "Paused for memory. Dispatch resumes it when swap falls." or "Paused. Resume it when you are ready." Run Claude and Start another session are disabled on a paused card, with the title "Resume the loop first".
  • Memory refusals. A memory refusal now shows "Not enough memory to : . Try again after a loop finishes." on start, group start, retry, restart and resume. Before this release, it showed "Couldn't reach the server". A group start no longer shows "no longer eligible" for each 409.

Evidence: "23 of 23 UI cases pass. Every copy string matches the PRD." (PR #260)

Known limits

  • You cannot approve a permission prompt from the attention queue. Open the terminal.
  • The queue refuses a reply in four cases. The session shows a permission prompt, the session shows the usage limit dialog, the session waits for the usage reset, or Claude exited.
  • A reply of more than 500 characters goes to the loop as a file with a one-line pointer.
  • The queue has no length limit.
  • A Needs input item can take up to about 2 seconds to show, because the marker watcher reads the panes every 2 seconds.
  • The sidebar Dashboard row and the Go to Dashboard command show only on a board with a main orchestrator.
  • When an orchestrator start fails, the reason can be raw text from the Claude pane.
  • A wake line clears a message that you typed but did not send in the orchestrator terminal.
  • A review session runs with the default skip-permissions argument and your gh login. "Post nothing" is a prompt instruction only.
  • A Roadmap Loop session starts with Refuse for messages from other sessions, but a later Resume of it starts with Accept.
  • A session with no hooks gets no settings file, so the messages setting does not apply to it.
  • A review card in Done cannot start a new review after the pull request changes. Ship from a review card pushes the card branch, not the review branch. A second session on a review card fails with a general start error.
  • Dismissed Inbox items never expire. Dismissed (n) ignores the search and the filters. A second dismiss within 10 s replaces the first Undo.
  • The command centre conversation reads all the answered decisions of the board every 3 seconds and filters them in the browser. No load numbers exist.
  • The command centre has no Markdown for orchestrator text, no message search and no paging of older messages.
  • A double click on Try resume again sends two requests. A double click on Save policy or Add orchestrator also sends two requests.
  • Urgent mode can make about 30 usage reads in one rolling hour on a steady climb. A 429 then holds the account, and a limit screen still moves the sessions.
  • The 429 hold is in memory only. A restart reads every account at once.
  • A manual usage refresh inside a 429 hold shows "Wait 30 seconds between refreshes.", also when the hold is longer.
  • resume_card can resume a loop that a human paused. This is a design decision.
  • A server restart forgets the pause run. The paused mark on the card stays, but the swap baseline, the below-ceiling count and the 5 minute cooldowns start empty. So the first reading above the ceiling after a restart can pause another loop.
  • The governor runs on macOS only. Pause and unpause by hand also work on Linux.
  • The lowest swap ceiling among the boards with running loops applies. It can pause a loop on a board with a higher ceiling.
  • A pause can stop an idle loop. A stopped process tree can go to swap, so swap can still increase after a pause.
  • A paused loop that moves to Done stays stopped until the Done cleanup kills it.
  • The dashboard progress row has no paused marker. Its count shows a paused loop as running.
  • Scenario 8 (memory pause end to end) runs by hand, not in CI.
  • The check takes about 15 minutes in CI since the timing split.
  • start_ship refuses a card in Done with card-not-done, not with already-shipped as the tool description says. No wrong PR or merge can occur.
  • The ship flow merges into main. A single card on a board whose base is a different branch is not proven.
  • release_prep finds a duplicate release card by its title only. Two repositories on one board cannot have an open release card at the same version.
  • At 768 px and wider, the floating centre alert covers the Terminal and Conversation tab labels while it shows a failed orchestrator start.

Upgrade notes

  • The store schema stays at 3. board-db.ts did not change, so there is no migration. The card paused mark and the two policy fields are new optional JSON fields.
  • Two defaults change for an existing install on macOS. A board policy with no memory fields reads memoryFloorMb 1024 and swapCeilingMb 8192. After the upgrade, Dispatch refuses a loop start when free memory is below 1024 MB or swap used is above 8192 MB. The governor pauses the newest loop when swap is above 8192 MB. Set the floor to 0 to turn the floor off. The ceiling accepts 256 to 65536 MB.
  • On the next start, Dispatch writes the Release playbook (release.md) into the playbooks folder one time, only when no playbook with that slug exists. A Release file that you edited stays.
  • The board page has no side panel now. Old panel=orchestrator links open the command centre.
  • For contributors: npm run check runs npm run test:timing as its last step, and npm run test waits for the check slot. When a new test starts a real tmux server or asserts on real elapsed time, add it to TIMING_FILES in scripts/run-tests.mjs.
  • Otherwise there is nothing to do on upgrade.

See it

dispatch-v4.5.3-memory-scheduler.mp4

Dispatch v4.5.3: the project map with the memory line for free memory and swap, the command centre Settings sheet on the Policy tab with the Free memory floor and Swap ceiling fields, a running loop card with the Pause button next to Reset, and the way back to the board

Higher-quality video with narration: dispatch-v4.5.3-memory-scheduler.mp4

Full changelog: v4.5.2...v4.5.3

Requires Node ≥ 22.22, tmux, ttyd, git, and the Claude Code CLI. Setup is in the README.