the engineering log

A Voice Is a Control Surface

Skyflo 1.2 lets you talk to a mission while it runs, and the voice drives the product rather than describing it. The hard part is not speech recognition. It is deciding, sentence by sentence, whether what you said was work or a control.

Read the full page:How Skyflo runs a mission, and learns from it
·10 min read·voiceharnesscontrol-planemanaged-inference

Two Kinds of Sentence

Everything you do in Skyflo is one of two things.

The first is work: read this file, change that function, run the tests, click the sign-up button on that page. Work goes to the mission harness, whose tools reach the terminal, the files, the web and the in-app browser. It is planned, approved where your access mode says it must be, and recorded.

The second is control: switch to plan mode, use Codex for this, close that tab, archive this mission. Control is chips, menus and buttons in the window. It changes how the work is done, not what the work is.

When you are typing, the difference is obvious, because the two live in different places. You type work into the composer and you click control with the mouse. Voice collapses that distinction the moment it exists. "Open the terminal" is a sentence. Is it a button, or is it a job?

We got that wrong first, in the way these things are always got wrong. Handed to the agent as a message, "open the terminal" was work, so Skyflo did the work: it opened Terminal.app. Which is, to be fair, a correct reading of the sentence. It is just not what anybody meant while sitting inside a product with a terminal pane in it.

So voice in Skyflo is built around that split rather than around speech. There is a control reader with a fixed vocabulary, and everything the control reader does not claim is work and goes to the harness.

Anchored at Both Ends

The control vocabulary is the app's own controls, said out loud: turn modes, orchestration strategies, lead selection, runtime, model, effort, browser tabs, find, workspaces, mission filters, settings categories, sidebar, theme, interface zoom, text size, the queue, memory review and memory settings, notifications, updates, archive, restore, rename, delete.

Two rules make that list safe.

Every pattern is anchored at both ends, and read from a sentence with the politeness stripped off. A keyword matched in the middle of a longer sentence is exactly how a request for real work gets swallowed by a control. "Can you check whether plan mode is what the docs recommend" contains the words "plan mode". It is not a request to switch to plan mode.

A sentence that is not exactly a control is work. The default is the harness. The control reader has to earn the sentence; it never gets it by proximity.

That is the whole design, and it is deliberately unglamorous. There is no model deciding whether you meant a button. A fixed grammar claims what it can prove and hands everything else to the part of the system that is built to plan, ask, and record.

It Operates the Product

Once the split is right, the useful part follows. The voice does not describe what it would do. It does it.

It opens the pane it is talking about. It starts a new mission and queues follow-up work. It drives the in-app browser: new tab, close, next, back, reload, zoom, or the third tab by number. It runs terminal commands. It changes the model, the effort, the lead and the strategy for the turn you are in the middle of. It walks memory review and accepts, dismisses or defers what is waiting. It changes its own voice when you ask for a different one by name, mid-conversation, and the conversation carries across the change instead of restarting.

Two behaviours matter more than the list.

It stops the moment you talk over it. Not at the end of its sentence. Interrupting a person is how you steer a conversation, and a voice you have to wait out is a voice you stop using by the second day.

Irreversible controls are still confirmed. Deleting a mission by voice asks first, because the fact that a sentence was easy to say is not a reason to make its consequence easy to reach. Terminal authority is revalidated after the command is prepared rather than only before, so a spoken command cannot ride on permission that was true a moment ago.

Voice did not get its own authority model. It reaches the same controls the keyboard reaches and the same harness the composer reaches, under the same approvals. That is the claim worth making about a voice interface in an engineering tool, and it is the one most of them cannot make.

The Models Came With It

The other half of 1.2 is quieter and changes what a plan is.

Until now, Skyflo ran on keys you brought. That is still true and still supported, and the free plan keeps it. What is new is that a paid plan includes the models: Core, Pro and Max each carry a monthly allowance, and there are seven managed models across three providers behind it. Luna, Terra, Sol and Astra from OpenAI, Gemini 3.8 Flash from Google, and GLM-5.3 and GLM-5.3-Flash from Z.ai. Sol and Astra are the models the higher plans exist for; Astra can be pinned on Pro and runs under Auto on Max.

Two consequences are worth naming.

Adaptive ranks from what the mission needs, not from a price list. This is easy to claim and easy to fake, so here is what it actually does, measured on the packaged app with the model paired to the turn from the daemon's own record. On Core a trivial mission chose GLM-5.3-Flash, the cheapest candidate, which is correct there and is the only row where cheapest wins. A hard mission on the same plan stepped up past cheaper candidates to Gemini 3.8 Flash on difficulty alone. On Pro a hard mission chose Sol, because Pro is pin-only for Astra. On Max a hard mission escalated to Astra, the most expensive model in the portfolio. And on Max a trivial mission excluded Astra, with the reason given: the maximum tier is for work that justifies it. A price ladder does not produce that table.

What you have left is capacity, and no amount of money appears anywhere. The Usage page carries two meters, because there are two things and they are not the same. Mission capacity is loop turns: every plan has them and every lane spends one, so it reads the same way on Free as it does on Max. Managed capacity is the pot that managed model turns and Voice Mode draw on together, and only a paid plan has one at all. It is a percentage and a reset time. No figure in currency is shown on that surface and none is sent to it.

A plan with no managed lane gets no managed meter rather than a meter reading zero, which sounds like a detail and is not. A meter at zero says you used it up. That is a different sentence from you never had this, and the second one is the true one.

Underneath, the accounting is exact even though you are not asked to look at it. Every settled figure is recomputed from the provider's own reported counters at the price revision the attempt reserved against, and the two count kinds stay distinct: an exact input count where the provider reports one, a tokenizer count with its own margin where it does not.

An Engineering Lesson From Getting Here

The best defect this release produced was one line of SQL, and it is worth the paragraph because of how it hid.

Skyflo reserves managed spend before a turn, then settles it after. A reservation that is abandoned, because the app was closed or the network dropped, expires on its own. The figure that answers "what is left in your allowance" summed every reservation in the active state, with no bound on expiry.

That looked harmless, because the path that takes a reservation sweeps expired ones to expired in the same transaction, a few lines before it reads the figure. Turns were always safe. Voice reads the same figure without sweeping, and there is no background sweeper.

So one abandoned hold made Voice refuse to start, saying the allowance was used up, while the Usage page in the same app reported the same allowance as free. Measured against a real database: one settled dollar, one two-dollar hold abandoned twenty-five minutes earlier, a three-dollar Core allowance. Voice said nothing left. Usage said two dollars.

The fix is a bound on the expiry in the sum. The lesson is the one underneath it: two surfaces reading the same rows must not be allowed to disagree about what is spent, and "the caller happens to clean up first" is not a property, it is a coincidence that held until a second caller arrived.

Two more from the same convergence, both of the same family. The daemon computed the Adaptive hints correctly and then dropped them on the wire, because a hand-written mapping omitted one member, so the account plane ranked with no mission facts and Adaptive quietly degraded to a static cheapest-first ladder. And Voice reported a remote close as the granted ceiling, so it told people their time was up when it was not. Each of those is small. All three were found by running the packaged app against a real database and reading what the ledger said afterwards, rather than by reading the code that was supposed to be producing it.

Where the Edges Are

Voice Mode and the included models need a paid plan, and the paid rollout is invitation-only at first, for verified businesses in India and the United States. A voice session runs up to thirty minutes in one sitting on every paid plan; what differs between the plans is the allowance underneath, which is what actually decides how long you can talk in a month.

The free plan is unchanged and is a real free plan rather than a decorated paid one. Missions run on your own provider keys, and the managed models are absent from the picker rather than shown and disabled.

What This Is Part Of

1.1.0 made Skyflo a HyperAgent: it learns from the missions it completes and can change how it routes work, inside a root layer only a signed release changes. 1.2 does not change that boundary. It adds a second way in.

The control plane's job is to sit above the agents, models, repositories and tools a team already runs, and to orchestrate, govern and record that work. A voice that operates the actual product is that same layer reached a different way, under the same authority, producing the same record. The models coming with the plan is the same idea applied to inference: the layer that governs the work can now fund and meter it too, and show you what is left as capacity instead of handing you a bill at the end of the month.

You can say it now. It still gets written down.