Ember is the household assistant: a bot in the family’s self-hosted chat rooms with a language model for a brain. The earlier post in this section is about one failure, a second account that had a voice and no ears. This piece is the long view: what she was for, what we measured when agents started working for her, where the design stands in October 2026, and which shapes were kept and which were dropped. The person who approves things is called the playtester here, because that is the role he plays in the rest of this estate.
The goal
The first goal, in the summer of 2026, was small and domestic. A bot in the family rooms that answers when spoken to, renders a few cards of her own for weather, scores and news, keeps a per-room cooldown so she does not dominate a conversation, and answers only people on an allowlist. She ran as one always-on container that made only outbound connections, with her chat brain’s tools switched off so that talking to her could never become running something. The brain was a coding-assistant subscription, not a metered API key, which set a budget shape that mattered later: the limit was a weekly quota of attention, not a bill per token.
The second goal grew out of the first within a month. If the assistant already lived in the rooms where the family talked, she could also be the one place where work on the house was asked for, approved and reported. By September that had been written as three roles: Ember knows, a reasoning seat thinks, and a worker does. By mid-October it had collapsed further into one sentence that every repository in the estate now carries as its goal: Ember holds one queue; a box is one worker with one name; a worker has capabilities; a capability is a module, installed, health-checked, advertised, and consumed by exactly one task kind. “Done” for any piece of work is written in the same vocabulary: merged, deployed and verified live on the box, or parked as an issue.
What we tested, and what the numbers said
The second account. We added a “dev” account so a code worker could post in its own voice. It got a presence dot and a script that posted as it. Then a request addressed to it went nowhere while the dot stayed green, because nothing read that account and the bot’s own rule said an @ to someone else is not an @ to her. The decision on 10 September was that the three roles are three tool sets, not three listeners: Ember is the one listener and puppets the dev account; a finished task is a package card in the ops room; one reaction merges, one declines, and a third merges and raises the deploy ask; and only the playtester approves. That story has its own piece.
Work as pull requests. On 18 September every repository moved to a self-hosted forge, ten converted from mirrors and four created, each with a copy kept on the public host. From then on any work the assistant was asked for came back as a pull request with its link on the card, and her merge was the merge. The move caught two traps: a mirror four commits behind would have force-pushed the public copy backwards, and the converter’s refusal to proceed until both sides matched is what found it.
A day of agents, in tokens. On 12 October we measured one day of the coordinator and its lanes: 4,377 model calls, 883 million cache-read tokens and 75.4 million cache-write tokens. Of the writes, 83 % (63 million) came after an idle gap of five minutes or more, which is the cache’s lifetime, and 52 million of those came from 230 calls that waited nine to eleven minutes, exactly the shell timeout. A rule from the week before, “lanes never poll”, had cut the number of calls and raised the number of tokens: every long wait outlived the cache and re-wrote the whole context. The amended rule is a wait of at most four minutes, and the lesson is to measure a rule in the unit of the bill. The same day found a coordinator session that had been resumed for twenty days (9,415 calls and 155 images read), 51 background shells of which 21 were identical, and a memory index that had silently truncated and dropped a rule. A coordinator now restarts at 150 thousand tokens of context or at the day’s end, from a runbook written before it stops.
Which model does the work. A bake-off ran the same thirty-six small tasks on the smallest model and the mid-sized one: thirty-six of thirty-six passed at one twelfth of the cost. Execution lanes now run the mid-sized model, small fixes and summaries the smallest, and the largest models are reserved for design, research and hard debugging, asked for each time. A local fallback was tried too: an open-source coding agent on a local model finished a 39.8-second task on one box, and a longer run of fifteen minutes and 984 thousand tokens produced a mergeable pull request. It is the fallback for specific, non-urgent tasks with an acceptance script, not the fast path.
The level-set. On 15 October we stopped and audited the whole orchestration layer because it was, in the playtester’s word, spiralling. The audit found eleven duplicated mechanisms: capability names declared in four sources, a painter service with four clients and two launchers, three different ways to hold a GPU lock, a speech module with no installer, and an agent framework used as an executor when what was missing was an installer. The one-sentence concept above came out of that afternoon, with a module format (dependencies, install, remove, units, config, health, advertise) and a goals file per repository that states each goal, its measure, and whether it is coded, merged, deployed or verified.
Where we are
Ember holds one file queue and is its only writer. A task names what it requires; a worker takes it when the requirements are a subset of what it advertises; priority is per capability; heartbeats say the worker is alive, and a separate check says the task ran, because a heartbeat proves the worker, not the task. Task kinds today are redeploy, speech to text, text to speech, voice samples, development work, and image painting. A box enrols with a one-time ticket, advertises the intersection of what it consents to and what it detects, and bootstraps from a signed release. Deploy asks are cards, approved in chat or in the admin console; the deploy script refuses on a red test suite; the live version is read from the health endpoint, never from the announcement.
The admin console is a small client-rendered app with a home view of what waits on the playtester, pages for agents, the GPU, the work pool and spend, and a kill button that acts by process id. Merges and deploys happen only on his click. Questions that need a decision go to a web questionnaire with evidence attached. Every post Ember makes is signed with the model that wrote it.
Two things are merged and not yet live, and the goals files now say so in those words: an approve-and-decline row on the home view, and the painter reached through image tasks on the pool instead of by address.
The relay that feeds the desktop tray had been crash-looping since 18 October, tens of thousands of times, because one module could not import another when run as a script; the tray sat grey. The fix, a fallback import and the missing file placed beside it, merged on 21 October.
What is next
Deploy the merged work and verify it from the box. Add a test that the names a worker advertises agree with the names a task requires, because a worker that called itself one thing while the pool called it another stranded a redeploy for a day. Finish the module path so that a machine fix is a module and a live job is its test. Encrypt the rooms Ember reads. Make question cards selectable in chat. Keep the local-only path usable at any time, not only when the cloud quota is spent. The work Ember queues is the work the other journeys describe, so the architecture journey is where this piece joins the rest.
Architectures we tried
Kept: one listener with several voices; packages and deploys as cards with the ledger as truth; one queue with capability matching; modules with health and advertisement; a coordinator that restarts daily from a runbook; small models for small work. Dropped: a second listener on a second account; orchestration rules written as prose for agents to obey, replaced by a runner that enforces them; resident adapters per machine, replaced by queue kinds; three GPU locks, replaced by one with two slots and a yield file; a twenty-day session; waits longer than the cache. Considered and not chosen: a metered API in place of the subscription; an agent framework as the installer of the thing it was supposed to run.