Skip to content

Plan 323 P3 + P3b — models on this computer, diagnosed, managed, and changed by Helmsman with approval - #123

Merged
munchist merged 8 commits into
mainfrom
aflow/323-p3
Oct 17, 2026
Merged

munchist merged 8 commits into
mainfrom
aflow/323-p3

Conversation

@munchist

Copy link
Copy Markdown
Contributor

Plan 323 P3 and P3b, plus D17 and D18, and a test fix. Plan: docs/plans/323-models-that-run-on-this-computer.md.

What it does

D17: loading shown in the chat

While a turn waits on a model on this computer, the chat's activity row says so in place of "Working…": "Loading Gemma 4 26B (this computer) into memory…", then "Reading the conversation…". The first thinking or text token replaces it.

  • How the end of a load is known: the Ollama adapter reads /api/ps. If the request would load the model, the adapter loads it first at the request's window, so the load has a visible end.
  • New channel: the lines travel on a new status live-delta channel.

D15 and P3: one diagnosis, and the models on This Computer

  • The record: LocalRuntimeDiagnostics and diagnoseLocalRuntime. Every issue carries a cause with its numbers and a remedy, which is either an action or an instruction.
  • Readiness: derives localFit and localIssues from the record. An error issue, such as Ollama down or a role's model not downloaded, counts against ready, and the banner points at This Computer.
  • New page section, "Models on this computer":
    • issue cards whose buttons run their remedies;
    • downloads with progress, stop and resume (one per runtime and tag, a second request joins it, and a download too large for the free disk is refused before it starts);
    • load, unload, use for every role, and delete.
  • Shared code: routes, readiness and Helmsman share @aflow/cybernetic-runtime localModels/.
  • Host inventory: publishes free disk under the model store.
  • Onboarding (D18): offers the models on this computer. In the local edition, a tenant following the platform recommendation may assign them.
  • Cost: a run whose every model call ran here reads "Cost: local".

P3b: Helmsman reads and asks

  • Two operations: model.runtime.status and model.runtime.change, in a model step type that only the local edition composes. They run inline in the orchestrator.
  • Who holds them: Helmsman can discover both, and its status read is pinned on the local edition. Runners are excluded, and skills that reference them are rejected.
  • Capability: migration 220 grants the new model.runtime group to the system profiles.
  • Approval: a change parks on the write-approval pause as a third target, local_model, with its own Action Center card. A space admin answers it.
    • The request: rebuilt from a fresh diagnosis whenever the step runs. Its hash covers the change and whether the asking conversation's model is the one changed.
    • Spending: the approval is spent once, through spendWriteApprovalGrant, which browser actions now share.

Test fix

streamRetention's consumer-hygiene tests walked every job stream on the Redis the live stack shares. They reaped the stack's idle consumers and deleted its eval job stream. cleanupStaleConsumers now takes the groups to visit, and the tests use a stream of their own.

Not built (stated in the plan)

  • the trial load for an unpaired machine, beyond Load now;
  • recent tokens per second in the diagnosis;
  • the approved one-time install;
  • issue codes on local-model tool errors.

Helmsman cannot assign a role's model. That goes through the space update, which no executor reaches, so Helmsman tells the operator to do it.

Review findings left as they are

  • Spent browser approvals reset at deploy: the spend key moved from aflow:browser-ask:spent: to aflow:approval-spent:, so for up to an hour after deploy a browser approval spent before it reads as unspent. The host's ACL gains ~aflow:approval-spent:*, and a host executor running new code before the server has re-applied ACLs gets NOPERM on a spend until the server restarts.
  • Stale approval reuse: an approval whose dispatch re-parked under a different hash stays unspent until its TTL. With the hash now only the change plus whether it reloads the asking conversation, that needs Helmsman's role model to change between the ask and the approval.
  • Repeated loading reports: servesWithoutLoading compares /api/ps context_length with the window on llama.cpp. If the runtime ever reported a different figure, each request would report loading and send an extra (no-op) load. Not seen in the tests against 0.40.5.

Testing

  • yarn typecheck and the web-product build pass.
  • yarn test:changed passes except aflow-executor-host checkoutReplacement ("cannot be made from inside the sandbox"). It also failed before this branch's host changes and imports nothing it touches.
  • The diagnosis was run against Ollama 0.40.5 on the 24 GB M3. It reports the measured Gemma build as verified, a 16K fit, and the spill at 32K.
  • Not yet run on the dev stack: the page, a download, the Helmsman approval flow, and the chat loading line.

🤖 Generated with Claude Code

munchist and others added 8 commits October 16, 2026 22:46
…in the chat

The Ollama adapter reads /api/ps before a request whose caller listens for
runtime phases; when the request would load the model it loads it first, at
the request's window, so the end of the load is observable. The agent turn
writes each phase to a new 'status' live-delta channel, and the chat's
activity row shows it in place of 'Working…' until the first token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LocalRuntimeDiagnostics in @aflow/schemas and diagnoseLocalRuntime in
@aflow/ai-client: installed and lineup models with fit, what is loaded and how
much of it is on the GPU, the build against the measured one, and issues each
with a cause carrying its numbers and a remedy that is an action or an
instruction. The runtime client gains version, installed, pull and remove; the
host inventory publishes free disk under the model store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…om the UI

- @aflow/cybernetic-runtime localModels: resolveSpaceLocalRuntime (moved from
  the server so Helmsman's operations reach the same runtime),
  diagnoseSpaceLocalRuntime, applyLocalModelAction (pull with a disk check and
  one claim per runtime and tag, delete, warm, unload, set the window), and
  cancelLocalModelPull.
- @aflow/redis localModelPulls: the pull's claim and progress in one record,
  renewed by its progress, with the cancel read on the same round trip.
- Routes: GET /spaces/:id/local-models (the diagnosis), POST .../actions (a
  LocalModelAction), DELETE .../pulls/:model.
- Readiness derives localFit and localIssues from the diagnosis; an error issue
  counts against ready, and the banner points at This Computer.
- This Computer gains 'Models on this computer': runtime and machine, each
  issue as a card whose button runs its remedy, and per-model download with
  progress and stop, load, unload, use for every role and delete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… computer

- In the local edition a tenant following the platform recommendation may
  assign the catalog's models on this computer (allowedModelIds takes the
  edition); a tenant that named its set is held to it.
- Onboarding offers them beside the hosted models. Choosing one puts every
  role on it, the Clerk included, adds an Ollama connection unless one already
  resolves, and skips the key step.
- A run whose every model call ran here reads 'Cost: local', and so does each
  such step, rather than showing no cost at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… plan 323 D18 and P3 state

cleanupStaleConsumers takes the consumer groups to visit, every executor job
stream's by default. Its two real-Redis tests walked every job stream of the
Redis the live stack shares, so they reaped the stack's idle executor
consumers, counted them, and deleted its eval job stream; they now hand
hygiene a stream of the test's own, which keeps the pending guard exercised.

Plan 323: D18 (local models assignable under the recommendation in the local
edition) and what P3 built and did not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nging it

- model.runtime.status and model.runtime.change, in a 'model' step type whose
  lane only the local edition composes; inline in the orchestrator over the
  shared localModels code. Capability group model.runtime (migration 220),
  bundle local_models, discoverable by Helmsman with the status read pinned on
  the local edition; Runner-excluded and refused in skills.
- The change parks on the write-approval pause as a third target,
  'local_model', through payload, blockedOn, Action Center item and card, and
  the chat notice. Its request is rebuilt from a fresh diagnosis on every run
  and its hash covers the impact — requests in flight that an unload or delete
  ends, whether the asking conversation runs on the model, the change's own
  figures — so a changed impact asks again.
- spendBrowserApproval becomes spendWriteApprovalGrant, the one spend for any
  target, under aflow:approval-spent: which the host's ACL may write.
- onboarding-flow.tsx stays under the 1500-line ceiling: provider data and the
  model card move to their own modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Free disk and a loaded model's cache size change continuously; in the hash, an
approved change would find no grant for the request it now makes and ask
again. The hash covers the change and what it ends; the figures stay on the
card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…vive silence

- A model-runtime approval item carries resolverPolicy ['admin'], the role the
  operator's own routes for these changes require; a write member could
  otherwise approve a delete or a window change they could not make directly.
- The approval hash keeps the change and whether it reloads the asking
  conversation; the runtime-wide count of requests in flight, which no lease
  ties to a model, moves to the card only, worded as what it is.
- A pull's lease is ten minutes, outlasting the runtime's silent digest
  check; every progress write carries the status, so a lapsed record is
  rebuilt whole; a stream that ends without 'success' is a failed pull, not an
  installed one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@munchist
munchist merged commit 05bc0e3 into main Oct 17, 2026
3 checks passed
@munchist
munchist deleted the aflow/323-p3 branch October 17, 2026 10:56
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