Skip to content

Changelog

Notable, user-facing changes to The Aetherium Engine, newest first. This page mirrors CHANGELOG.md in the Engine's source repository and is kept in step with it automatically; internal-only changes (refactors, tests, housekeeping) are omitted.

The format is based on Keep a Changelog. The Engine does not yet cut tagged releases, so entries are grouped by date.

2026-10-29

Added

  • A llama-server preset for the dense Qwen3.8-27B (qwen3.8-27b) on the unified-memory slot: a 131,072-token window, one slot, speculative decoding with the model's own multi-token-prediction head, and the model's own sampling. It is not part of the standard set; a run that names it loads it.
  • Steam Frame device tools. Four MCP tools in a new device server group act on a Valve Steam Frame headset. frame_status reads its state (Standard). frame_install puts a build on it, frame_run runs the app while someone wears the headset and brings back the log, the performance recording, the saves and a contact sheet, and frame_grab takes one picture of the headset view; those three are Restricted and wait for a human's approval. Only the steam-frame-vr roles are given the group. Connection settings live under Aetherium:Devices:SteamFrame. Captures land under .frame/ in the workspace and are never offered to or indexed in a knowledge base.
  • A steam-frame knowledge pack (the headset, the Verified bar, Unreal Engine 5.8 settings, the performance ladder), searched by the steam-frame-vr team.

Changed

  • reasoning_effort accepts xhigh, for a team's worker turns and for AI_ReasoningEffort. Qwen3.8-27B's chat template takes xhigh, medium and low; before this, xhigh was treated as unset and turned thinking off.
  • The per-call model timeout (AI_RequestTimeoutSeconds) can be raised to 4 hours, up from a 1-hour ceiling, for models that write a large file in one call. The default is unchanged.
  • steam-frame-vr targets Unreal Engine 5.8 on the Steam Frame: C++ with Valve's OpenXR extension and a standalone Android APK for the headset at 72 Hz. The role prompts are rewritten, and the asset pipeline engineer gains voice lines (tts_render, tts_status). The container cannot build Unreal or reach the headset, so the prompts hand those steps to the operator and the device tools.

Fixed

  • The device tools refuse a symbolic link anywhere on a path they read or write, so no host file reaches an agent through one.

2026-10-28

Added

  • read_file reads a line range. start_line and end_line return those lines numbered (990| …) under a header with the file's line count, counted the way edit_file counts them. A ranged read is never offloaded; the notice for a whole file too large to show gives its line count and how to read a range of it.
  • A final write phase. A turn that reaches its round cap, or eight read-only calls in a row, while a file is still owed or during a repair now gets two calls in which only its write tools run, instead of a closing reply with tools off that nothing saves. Checkers, re-plan turns and agents that owe nothing end as before.
  • Placement warnings in the resolve preview. POST /api/template/{id}/resolved and /api/template/resolve warn when a node is placed on a slot the health check has marked offline, or on a slot whose model list does not include its model.
  • A page with no code of its own fails its gate. An owed HTML page that loads scripts from elsewhere but has none of its own is sent back naming the libraries it loads. Pages whose only script works alone (the Tailwind Play CDN and similar) are exempt.
  • The page-request gate fails a page no URL serves, and a page whose script throws on load ([PAGE-UNSERVED], [PAGE-LOAD-ERROR]). A file that failed to load stays the asset check's finding.
  • The page-throws gate quotes the page lines around each error's line ([PAGE-SOURCE …]).

Changed

  • Workers check, fix and re-check. The worker guidelines replace "verify once, then stop" with checking the step's own checks, fixing what fails and checking again until they pass or the tool calls run out. This changes harness behaviour: results before and after are not comparable.
  • Single and Sequential gate repairs run as repair turns, as plan-path repairs already did, so a repair is no longer told its task is complete after its first write.
  • WRITE NOW removes every read tool (read_file, list_files, search_files, read_logs) until the agent writes.
  • The path guard learns declared paths from the step's instruction only, not from role prompts or the Engine's own examples, and defers to the owed-file check when the step owes a file of the same name.
  • The cut-off plan nudge says what a step receives: the request, its own step and recent results, not the project config.
  • A checker keeps its shell on a repeated-call streak and is told once to run the check differently or report NOT RUN; a checker's repair turn keeps all its tools.
  • The plan contract gives declared files no step owes to the node's last step, and leaves a node alone when one of its steps has no list.
  • The step verifier sees the node's declared outputs that exist, not only the files the turn wrote.
  • Faint-text repairs carry the page-request finding with them and name the colours measured.
  • key_out cuts out shaded magenta grounds, not only flat #FF00FF, and keeps a dark piece that touches its shadow.
  • The [DETERMINISM] log says server-default when the request carries no thinking control, instead of off.
  • A failing backend probe logs once per state change, without its stack trace; repeats go to Debug.

Fixed

  • A QA report citing a CDN address or an importmap module name (three@0.160.0/…, three/addons/…) is no longer read as citing a missing workspace file.
  • run_shell with background: true refuses commands its wrapper would drop (a cd or export prefix, &&, ;, a trailing &) instead of reporting a start that never happened; sh -c '<script>' passes.
  • A send-back re-check writes a fresh verdict instead of re-reading its old FAIL report, and reports set aside are restored when setting them aside fails.
  • Cross-reference checks resolve a root-relative script path against the static folder and skip scripts no page loads.
  • A citation matches by folder suffix only when it names a folder.

Fixed (later the same day)

  • The Engine no longer follows links in a workspace. Every file the Engine itself opens on its host from an agent's path or from workspace content (voice-over and image saves, download_pdf, capture_reference, the page checks and their static server, the deliverables routes, finishing files, send-backs, step snapshots, verifier and run-close reads) is refused when any part of the path below the workspace is a link, and the containment check is bounded at a path separator. A deliverable that is a link counts as missing; the preview and raw routes answer 404 for one. download_pdf treats a leading / in save_path as workspace-relative.
  • The final write phase also opens on the same-tool streak and duplicate-call exits, so a repair that searched the page seven times ends in two write-only calls instead of a reply with tools off.

2026-10-25

Added

  • A run-level page check before a planned run is finalized. Every page the run owes is read once more against the code the run finished with; a page whose requests fail there fails the run ([PAGE-REQUESTS] run-level). There is no final repair.
  • Pages are read again when the app's code moves. A step that changes the app's code reads the run's other pages again, and so does the re-check after a verifier repair. A failed reading stays due at every later check until one passes, and a failure on a page an earlier step wrote names the server's file for the repair.
  • The page-request gate checks a second action. After a first submit that succeeds it submits the same form again with a second entry, and after an add-to-cart press it checks that the page's main control is enabled; a control left disabled, or a second submit that sends nothing, fails the step. Text date and time fields are filled in the shape their placeholder shows.
  • A gate repair that is making tests pass gets more rounds. On a step with more than 12 tool rounds, each run_tests in the repair that improves on the last comparable run (more passed, fewer failed, no fewer run) extends the repair by eight rounds, up to the step's own budget ([REPAIR-EXTEND]).
  • A re-plan turn that leaves the failed step failed no longer strands the plan. The Engine sets the step back to pending itself, once ([REPLAN-DEFAULT]); a step whose files were kept under [STEP-KEEP] is marked skipped instead, so the step after it runs.
  • The step verifier sees what the Engine measured. When the Engine ran the step's whole test suite itself and it passed, the judge's prompt lists that run as fact, and a judge FAIL over it bounces the worker with a note to bring its report in line with the passing run rather than change code or tests.

Changed

  • The re-plan turn. Its prompt says it is a re-plan turn with four tool calls, and gives the failed step's full ask (cut at 600 characters) and its required outputs. The last call offers only update_plan and reply; update_plan may not set a step in_progress or completed in that turn, and a pending with its result ends it. Each call's output is capped at 4096 tokens, and the turn composes no closing reply.
  • More steps are verified. A step that reads as a check ("Test …", "Review …") and owes no planned output is now verified when it wrote a test file or a file its node owes. A test script definition in a step's words ("test": "node --test …") no longer counts as a promise of passing tests, and a run_tests with a pattern that filters nothing (*) counts as a whole-suite run.
  • Verifier repairs. A step's verifier repairs share one budget across its rounds, each repair restarts the step clock, a repair's own run_tests and validate_web_app results feed the next verification attempt, and a step timeout inside a repair is recorded as a timeout, so the re-plan and the re-run are told it timed out.
  • The page-request gate starts the app from a copy of the workspace, installing dependencies first when they are declared and missing, so its submits never write into the project's data. A 409 answer is not a failure. App page checks also install missing dependencies before they start the app's server.
  • Repair turns keep their tools. A repair turn whose owed files are all on disk already has no write phase; read_logs on a file counts as a read, and a repair turn that writes after being told to gets its read tools back.
  • A page step the plan contract adds goes after the step that owes the app's server, and depends on it.
  • Run review wording. A completed step whose verification failed reads "its verification failed", not "the judge failed it after its repairs".
  • A down inference slot is logged once. The per-probe failure is a debug line; the warning on the transition to unhealthy now carries the reason (no answer within 5s or probe failed). The slot is still probed every 30 s.
  • Verification scope plan warns. A team with verification enabled and scope plan verifies no step; the Engine now logs a warning saying so.

Fixed

  • read_logs no longer reads the Engine's host. With source: "file", a workspace or relative path is read the way read_file reads it and any other absolute path inside the team container (refused without one). Before this, the path was opened on the host, so container log paths failed and a rooted path or a symlink read host files.
  • A kept step recovered by a later one no longer fails the run. A [STEP-KEEP] failure is decided at finalization by a later completed step whose last run_tests passed at least as many tests as the kept step ran.
  • Verdict lines are read as written. The word "verdict" is never taken as the value, prose under a verdict heading is skipped, and a template line (PASS or FAIL, PASS/FAIL) states no verdict.
  • The keyboard play test fails closed when reading the page's window.game.state throws, naming the throw.
  • run_tests names a test script whose files do not exist (NO TESTS RAN — the test script names test files that do not exist) instead of reporting a bare exit code.
  • Repair notes for node:test failures carry the failure: each failing test with its expected and actual values and location, or the file's crash line; todo tests are not counted as failures.
  • A root-relative asset the app serves from the page's folder is not reported missing when the page checks fall back to serving files for an app that has a start script.

2026-10-24

Changed

  • A re-plan can re-open the failed step. After a step fails a check, the supervisor's re-plan turn now lists the failed step first among the steps it may amend and asks it to set that step back to pending with update_plan (with result saying what the re-run must fix) or to skip it. The re-run is told why its last attempt failed, and the planner's note.
  • Gate repairs restart the step clock and take at most 12 tool rounds. Verifier repair turns are back to 6 tool rounds (they were raised to 20 on 23 Oct).
  • run_tests runs the project's own test script. For a Node project it runs npm test (passing the pattern only to Jest or Vitest scripts); a project with no test script and no test files reports NO TEST SUITE, which is not a failure.
  • play_test plays a game. A page that sets up window.game is also put through the keyboard play test, and fails when holding the keys moves nothing.
  • The post-run audit sees the Engine's own test runs. A run's Completed or Failed status now follows the latest test verdict from any source, the verifier's included.
  • A node's output conditions survive the plan. When a planner names a step's output by bare path, the conditions the node's template attaches to that path still apply.
  • Page checks open an Express app's pages from the app's own server, so root-absolute asset paths resolve as the app serves them; a failed load names its path with the leading /.
  • The start check also runs on any step that writes package.json or the start file, and again after a verifier repair that changed either.

Fixed

  • The page-request gate stops its own server. Its stop command failed under sh (dash), so later checks met the first server on the port and judged stale code. It now stops the process group, frees the port before starting, and never judges a server it did not start.
  • Empty __init__.py, py.typed, .gitkeep and .keep files are files. They are no longer bounced as empty writes, removed, or reported missing by the verifier.
  • A plan step's gate repair can now repair. A step that fails its start check, page-request gate or required-output gate gets its repair round as a repair turn: the owed files already on disk no longer count as a finished job on the agent's first call, so the agent is no longer stopped before it changes anything.
  • The plan summary names each kind of deterministic verdict. It said "reported files missing from the workspace" for every one; it now counts missing files apart from the step's own tests or quality checks failing.

2026-10-23

Added

  • A step that owes package.json must start. On the plan path the Engine runs the step's npm start in the team container (after npm install when dependencies are declared and not installed) for up to 20 seconds; when nothing answers on the port project-config.md names, the step fails the start check with the start script's error lines, and its repair round reads them. A server already on the port is replaced by the probed one. No container or no node in the image passes the step.
  • A step that owes a page is checked against the app's own server. The Engine starts the app on the container's second published port, submits the page's first form or presses its main button in a headless browser, and fails the step when a same-origin /api/ request answers 4xx or 5xx, naming the method, path, status, response body and what the page sent. A page or app it cannot run (no start script, no published port, no browser) passes.

Changed

  • A failed step that moved its own tests forward keeps its files. When a plan step's only failure is its own run_tests, and the last run passes more tests than the first, fails fewer and runs no fewer, its files are no longer put back (logged [STEP-KEEP]); the re-plan is told which tests still fail.
  • Tool rounds. A plan step's turn takes the team's max_turns_per_agent, never fewer than 18 (it was always 18), and a verifier repair turn gets 20 rounds instead of 6.
  • The planning turn carries project-config.md. The supervisor's create_plan turn now includes the workspace's project-config.md (cut at 12,000 characters), since that turn offers no tool to read it; a planner told to read it first had been sending a plan with a goal and no steps.

Fixed

  • Disabled controls no longer fail the contrast check. Text in a :disabled control, an aria-disabled="true" element or a disabled fieldset is not held to contrast (WCAG 1.4.3 exempts inactive components); the rendered audit counts it as inactive.

2026-10-22

Changed

  • scripts/kv-cache-report.py says which way the tool list moved. A big cache miss labelled "tools changed" now reads tools changed: narrowed, widened or swapped (fewer, more or the same number of tools, from toolCount on aetherium.prompt.assembled), dropped (a call sent with no tools) or restored (tools back after a call with none). Relayed calls carry no count and keep the plain label.

Fixed

  • Runs a restarted Engine left open are closed at start. A build run still Running or Queued from before the Engine process started is marked Failed ("engine restarted mid-run: the process running it ended before it finished"), and its open steps close as Failed (Running) or Skipped (Pending), "engine restarted mid-step". An open step left from before the start closes the same way when its run has already finished. A reason already on the row is kept. Before, these rows stayed Running until someone cancelled them, and read as a run waiting on a model.
  • A cancelled run stays Cancelled. When the task of a run cancelled from outside finishes or fails after the cancel, it no longer overwrites the run's status, nor a step the cancel already closed; a cancelled finishing run still closes its gate.

2026-10-18

Added

  • Telegram notice targets. A webhook target with Kind telegram, a ChatId and a bot token as its Secret posts each event's message straight to that chat through the Bot API, with no gateway or model in between; events without a message are not sent to it.
  • Operator notices. A finishing gate now emits finishing.awaiting_answer when its brief is written, finishing.failed when a phase fails or a stranded pass is closed, and finishing.done when Phase 2 completes. These and tool.approval.requested carry a top-level message a person can read as it stands, ending with a link to the app page when the new Aetherium:Bridge:AppUrl is set, so a chat gateway can relay them with one template.
  • Heat gate for GPU service starts. VramOrchestration:ServiceStartMaxCelsius (default 0, off): a run or batch lease that would start a GPU service on a slot of the Engine's own machine (a slot with no card, or whose card has no Host) while the CPU package temperature (k10temp Tctl) is above the limit goes back to the queue, with the reading as its waiting reason, and starts once the reading is at or under the limit. A service that is already running is reused whatever the reading; interactive leases never start services and are never held. A run waits at the gate for up to 20 minutes instead of failing. Meant for machines whose GPU shares the die with the CPU.
  • Config backups on every deploy. scripts/deploy.sh copies each live *.Local.json to ~/.config/aetherium/config-backups/ (or $AETHERIUM_CONFIG_BACKUP_DIR), named with the UTC time and the previously deployed commit, mode 0600, keeping the newest 20 of each file. Copies left inside /opt/aetherium are deleted by the deploy's sync.

Changed

  • Policy-filled worker nodes write up to 8,192 tokens per response. The model policy's code and content slots now carry max_tokens 8192, filled onto a node that sets none; the team default of 4,096 cut off a frontend's write_file mid-file.
  • Hierarchical teams get longer default time limits. A team on the hierarchical recipe that declares no circuit_breaker now allows 900 s per plan step and 2700 s per session (was 300 s and 1800 s). A generated team's frontend step was cut off at 304 s while it repaired a page the browser check had failed. Teams with GPU services keep their higher floors.
  • Quick Create gives each kind of work its own role. The generator was told to design the smallest team, so a brief owing a server, a page and the tests that check them came back as one agent. It now asks for the team that delivers the request correctly: work of different kinds gets its own roles, and two or more such roles get a hierarchical team with team-lead, the size at which the Engine plans and verifies each step. A request that is one kind of work, or asks for one agent, still gets a single agent.
  • Policy-filled nodes run on the instance that serves their model. A slot in Templates/composition/model-policy.json now names, beside its model, the instance that serves it and the max_tokens it was run at, and a node filled from the slot (a library role, a generated custom role, a generated single agent) takes both unless it sets them. A filled node used to carry only a model id and ran on Primary. A node pinned to a different instance keeps its pin, gets no model, and the resolver reports it. The media pin moves the instance with the model.
  • Model policy re-pinned. The planning slot is beam-1.0-501b-a23b with a 16384-token cap; the code and content slots are qwen3.5-35b-a3b (content was "discover at run time"); media is unchanged. The shipped presets whose roles take their model from the policy resolve to these models too.
  • deploy.sh requires bun for the client build. It looks on PATH, in $BUN_INSTALL/bin and in ~/.bun/bin, and exits with status 7 when none has it, where it used to warn and deploy the server beside the previously built client. --no-spa deploys without the client build.

Fixed

  • Quick Create no longer fails while its placed slot is empty. When the TeamGenerator placement is unpinned and its slot has no resident model, the generator now tries LMStudio:OfflineFallbackSlot before Primary. It used to go straight to Primary and fail whenever Primary could not be reached.
  • A placeholder page no longer counts as delivered. An owed HTML page whose visible text says it is a stand-in ("this is a placeholder …", "replace with actual …", lorem ipsum) fails the step and goes back to its role.
  • Finishing gates no longer stay stuck after an engine restart. A finishing pass killed with the Engine used to leave its gate in Phase 1 or Phase 2 for good, and every new gate on the project was refused. On its next poll the finishing runner now closes such a gate as Failed, with a note naming the stranded run, and fails that run's row; a new gate can then be opened.
  • Card holds skip a slot that is known down. A hold used to fail whenever any slot on its card could not be probed, so a card shared by two slots on a dual-boot machine (one per operating system) could never be held. A slot known down (two failed health probes in a row) is now skipped, and the hold's log line names it; a slot that has failed only one probe still fails the hold.
  • Verifier repairs do the repair. A step the verifier sends back now runs as a repair turn: the files the step already wrote no longer count as the step being done, and the Engine no longer orders them written again. The repair ends on a reply without a tool call or after 6 rounds; before, a repair could end on the agent's first tool call.
  • Router model sizes. When a llama.cpp router's model status carries no path, the model file is taken from the --model/-m value in its arguments, so router models no longer size as 0 bytes. Measured ModelCosts still take precedence in the planner.
  • Dynamic-parallel subagents lease their own slot. A subagent resolves with its own subagent_defaults node, so its run lease lands on the slot that node pins rather than on the slot of AI_WorkerModelId.
  • The verifier installs dependencies before testing. In a Node workspace it runs npm install when node_modules is missing, before its own npm test.
  • The stale-narration check covers globs. When an audio step owes a glob or a folder, every matching render's .render.json is checked against the script.

2026-10-17

Added

  • Card telemetry for cards in another machine. A card with a Host is read on that machine over SSH through the GPU-node wrapper's new cards verb, which prints the machine's GPUs from sysfs. GET /api/inference/cards and the Inference page show its VRAM, temperatures, power, fan and links (not its processes). The read never holds up the page: the last answer is refreshed in the background once it is 5 s old and shown until it is 30 s old.
  • Z-Image Turbo as a team image model. image_model: "z-image" renders comfyui_generate on Z-Image Turbo (Apache-2.0) through a new leased GPU service, comfyui-zimage (ComfyUI on port 8190, address from the ComfyUI_ZImageEndpoint setting). It renders from text only — reference_images is refused with a reason — and supports ratio and transparent. The team designer offers it.
  • GPU services on a machine with no card of its own. scripts/render-gpu-env.py places a GPU service whose slot has no Card without a pin when no configured card is in the machine (its one GPU is integrated), so the voice and image services can run beside the Engine there. The voice launcher runs against the system ROCm when its environment carries no ROCm runtime wheels.
  • Video clips as an agent tool. comfyui_video renders a 1-5 second Wan 2.2 TI2V-5B clip (Apache-2.0) from a description, or animates a workspace image, and saves it into the workspace as an MP4. It runs on a new leased GPU service, comfyui-video (ComfyUI on port 8191, address from the ComfyUI_VideoEndpoint setting), and has its own video server group and tool profile, so no existing team's tools change.

Changed

  • Loopback-only routes refuse a proxied request. A request carrying X-Forwarded-For is refused by the [LocalOnly] routes (and the loopback-only query parameters of rag/search) even when it arrives from loopback, so a reverse proxy on the Engine's machine does not make every caller local.
  • Speech and image jobs no longer push chat turns off their slot. A lease that names only GPU services (the speech relay, an image job) runs beside the slot's server, so it neither refuses nor evicts an interactive lease. A run or batch lease that names a model still does.

Fixed

  • The Inference page files a card under its own machine. The slots on a card and the planner budget there come from config for every configured card, including one this machine's sysfs does not see, so a card in another machine is no longer listed under the Engine's machine as having no slot.
  • A voice render that hangs ends with a 504. POST /v1/audio/speech gives each render a budget of Aetherium:Tts:RelayRenderBaseSeconds (30 s) plus Aetherium:Tts:RelayRenderSecondsPerWord (0.5 s) per input word, never more than Aetherium:Tts:LineTimeoutSeconds. A render with no audio by then is cut off with a 504 that names the word count, the budget and how busy the card was, and the Engine logs the start of the input. A caller that hangs up cancels its render. Before, a hung render held the caller until the client gave up.
  • The inference queue answers at once while a machine is off. GET /api/inference/queue no longer probes a slot that is known down: its row reads failed with an offline note giving the last health probe, so the view no longer waits out a connect timeout (5 s per call). The Inference page polls five seconds after each answer instead of aborting a slow answer on a fixed beat.

2026-10-16

Added

  • GPU cards in another machine. Aetherium:Gpus:Cards:<card>:Host names the SSH destination of the machine a card is in. The arbiter starts and stops the GPU services on that card's slots over SSH, with the key Aetherium:Gpus:RemoteSshKey, through services/aetherium-gpu-node, a wrapper that runs only is-active, start, stop and write-env on the units the deploy published there. deploy.sh checks, syncs and restarts each such machine's pins, service units and routers on that machine (scripts/gpu-node-units.sh; render-gpu-env.py --host, --list-hosts, --export). The card's ListenAddress is the address its routers, ComfyUI and voice services bind (AETHERIUM_LISTEN; 127.0.0.1 for a card with no Host), and an optional nftables guard keeps those ports to loopback and the Engine's machine.
  • Side roles leave a slot that is down. With Aetherium:LMStudio:OfflineFallbackSlot set, an unpinned side role (compaction, summaries, lessons, code review, the verifier and judges, embeddings) whose slot failed its last two health probes runs on that slot instead. Pinned roles and team nodes stay where they are and fail fast.

Changed

  • A slot whose machine is off fails fast. After two failed health probes in a row a slot counts as down: leases on it are refused at once, smart routing and the re-warm loop skip it, and the scheduler stops probing a down Primary on every dispatch cycle. Connecting to an inference server is bounded (5 s for model management and the /v1 relay, 10 s otherwise), so a machine that drops the connection attempt costs seconds rather than minutes.
  • Every GPU pin file carries AETHERIUM_LISTEN. The first deploy of this change rewrites every pin and both router units, so both llama-server routers restart once and drop their resident models; the standard sets are restored afterwards.

2026-10-15

Added

  • The Inference page shows every slot by machine. A strip of tiles heads it, one per slot (status, resident models, memory, holder). Below, slots are grouped by the machine they run on, as rows that open to the detail. A card's hardware stays folded unless something needs a look, and schedules fold to a count and the next one due. A slot on another machine shows the memory the planner has booked for it. The new Aetherium:LMStudio:Instances:<slot>:Machine setting names the machine; the queue API reports each slot's machine, card, budget and costed memory.
  • Prefix-cache telemetry per message. aetherium.prompt.assembled carries a short hash and token estimate for each message of the prompt as sent and a hash of the tool list; aetherium.prompt.cache adds the processing time. scripts/kv-cache-report.py labels every large cache miss by the first message that changed (tool list, system prompt, history, or a lost cache).
  • Relayed chat calls are measured too. The /v1 relay logs one [RELAY-CACHE] line per call the inference server timed, in the same shape; scripts/kv-cache-report.py --relay reads them.
  • A llama-server model can keep its prompt cache across an eviction. With slot-save-path in its preset, the Engine saves the model's slot before unloading it and restores it after the next load, so the next turn that extends the saved prompt reuses it instead of processing it again.

Changed

  • Agent memory, knowledge-base excerpts and lessons head the agent's first user message instead of its system prompt. The system prompt stays identical across an agent's steps and restarts, so the inference server can reuse it; a Roundtable's earlier rounds moved the same way. Prompts differ from earlier runs: compare benchmark results only on one side of this change.
  • Context compaction leaves the system prompt as it was. The summary enters as a user message after it. Without a summarizer, the fallback cuts history once to 60% of the window, counting tool definitions, instead of trimming to 85% turn after turn.
  • The final-reply prompt after an agent's tool loop keeps the agent's tool list with tool_choice: "none", so it reuses the cached prompt; an answer that is still a tool call is asked again without tools.

2026-10-13

Added

  • A New project page that interviews a game request. "New project" opens its own page. A request that reads as a game is asked six fixed questions (kind, controls, places, how it ends, what can be lost, picture style) that fill a game card; any other request gets the needs checklist. Then check each part step by step (ending in an editable view of the exact brief) or go straight to the designer. Nothing calls a model until the team is designed.
  • A keyboard play test for games. The keyboard-playable clause holds a page to a window.game.state hook whose frame counter rises and whose player moves while → or D is held. The playable clause holds a page to at least one control that changes what it shows. A generated game owes keyboard-playable.
  • A canvas game starter kit. A team's starter_kit_dir names a workspace folder the Engine copies its kit into at project creation: frame loop, keyboard and touch input, pictures, camera, scenes and the play-test hook. A self-contained page may load it.
  • play_test, a QA tool that opens a page in a browser, clicks each control once and reports every script error. A QA role holding it cannot state a verdict on a page it has not played.
  • Game pieces with transparent backgrounds on SDXL. comfyui_generate with transparent paints the object on flat magenta and the Engine cuts the magenta out. In a game team the image role draws the player and collectables as separate pieces.
  • The base-role library is offered to the team generator by default, alongside the core roles; the generator endpoint's roleLibrary=false leaves it out.
  • A GPU service can run uncapped when it has the card to itself (AloneEnv), and the queue view shows how long a warm service has left.

Changed

  • Every page a step owes is opened in a browser when the step ends. A script error, a file that does not load, or text under 3:1 contrast sends the page back to the step that wrote it; a page that throws also fails its QA PASS and its run.
  • Charts are drawn by code from the data. A chart, plot or graph owed by the image maker moves to the team's data role, which draws it with matplotlib.
  • A node reaches the tool group of every tool it lists, not only its declared servers.
  • The 2K image re-render after each run is off by default; finished images are made in a finishing run, by request.
  • The chat relay refuses with 503 and a retry floor instead of 400, so a client's failover moves on at once.
  • The planner adds steps for jobs it left out, and verdict records keep the judge's own pass/fail beside the score.

Fixed

  • Trimming a conversation to fit the context window keeps user and assistant turns alternating, and later requests are fitted to the window the model server reports. Strict chat templates rejected the trimmed conversation.
  • Quick Create copies the starter kit too, and a path built in a template literal is left to the browser check instead of failing self-contained.
  • Idle models are woken again when an idle GPU service stops, and a planner entry that names a node's path without its clause gets the clause back.

2026-10-12

Added

  • GPU services can stay warm between runs. A service in Aetherium:VramOrchestration:Services takes an optional LingerSeconds (default 0, the old behaviour): once no lease names it, the unit keeps running that long, and the next lease that needs nothing new on the card reuses it instead of waiting for a restart. Any lease, or mid-run model load, that must load or start something stops every warm service on that card first, and the slot's standard set is restored only after they have stopped. Back-to-back runs on one team spent 76 s swapping instead of 225 s.

Changed

  • Pre-run prepares different cards at the same time. A run's slots on different cards are acquired concurrently; slots that share a card still go one after another, and a refusal still releases all of them.

Fixed

  • Image finishing keeps every image when the prompt enhancer loses its lease. The job used to re-queue only the images not yet rewritten, dropping those already rewritten but not rendered.

2026-10-11

Added

  • The Knowledge page shows the domains. A "domains" tab beside the documentation folders lists every domain the agents retrieve from: how many documents each holds, how many you approved and when, and whether a shipped template writes there. Opening one lists its documents, approved first, with project, run and approval date, and each opens to its full text. Documents an index rebuild carried over are listed too. Before this, an approved document disappeared from view after the click. New routes: GET /api/knowledge/domains, domains/{domain}/documents and domains/{domain}/documents/{documentId}.
  • A documentation pack can be downloaded again. Each pack on the Knowledge page shows when its files were fetched, and a GitHub-sourced pack offers "download again" (POST /api/knowledge/packs/{name}/refresh). The index job downloads beside the pack, swaps the new files in whole so pages the source deleted go too, then re-indexes the pack; a failed download leaves it as it was. The image packs now appear in the library list.
  • Pictures and recordings enter the knowledge base as text. The index embeds text only, so a picture is now indexed as a caption from a vision model (qwen/qwen3.5-9b by default, chosen by a ten-model bake-off) and a recording as a speech-to-text transcript made on the CPU. A completed run offers its pictures and recordings as candidates beside its text files, and approving one moves the file into the domain with its caption or transcript. The library accepts PNG, JPEG, WebP, WAV, MP3, OGG, Opus, FLAC and M4A uploads, and a hand-kept pack's pictures and recordings are indexed when it is built. Captions and transcripts are cached by the file's content, so an unchanged file is described once. The Knowledge page shows the picture or plays the recording beside its text. New routes: GET /api/knowledge/candidates/{projectId}/{documentId}/media and GET /api/knowledge/domains/{domain}/documents/{documentId}/media; settings under Aetherium:Media.
  • The Agent Library lists the roles teams are built from. The page used to list per-project copies of team agents that no run reads, and was empty by default. It now shows the role library: every role the templates name and Quick Create picks from. It opens on one card per team (name, role count, the roles and who leads them); a team opens to its roles, and a role to its purpose, model, the templates that use it, its tools and its system prompt, the long parts folded. Search by name, purpose or tool, or filter to supervisors, workers or the roles Quick Create may pick. The library is read-only. A template's roster on the Templates page links each member to its role, and the dashboard's "Agents" count (the copies) is now "Agent roles". New routes: GET /api/composition/roles and GET /api/composition/roles/{id}/node.
  • The Designer adds an agent from the library. from library adds a role to the team as an unsaved agent with its prompt, tools and model, numbered if the team already has an agent by that name. The agent keeps its role when saved (role_id in the team patch), so a reporting role stays read-only.
  • New project asks what the project needs. Once a brief is typed, the form shows a checklist — web front end, audio, images (drawn as the brief says, or as 8-bit pixel art, photographs, illustrations, or diagrams and charts), video, and 3D, marked not available yet. Each ticked need is written into the brief the generator reads, and a design that leaves one with no role able to make it is handed back once. POST /api/teamgenerator/generate takes the same as needs and imageStyle query parameters.
  • Creating a project shows what it is doing. A panel under the form names the running phase (designing the team, then creating the project) and counts the seconds.
  • A Completed run that needs review says so, and why. A run whose steps completed although the step verifier failed them or could not decide, or whose page shows fewer of its pictures than it owes, finishes Completed with review notes: the Studio's result card reads "run completed · needs review" in amber with the reasons, the Live tab's badge says the same, and the run's reviewNotes (new build_runs column) and the build.completed webhook carry them. Which runs are Completed or Failed is unchanged.
  • The Designer shows what a team knows and what each agent runs on. The knowledge chip names the packs the team will search and marks one that is not indexed, a "no domain" warning shows when the team has no knowledge domain, each agent names its model (its own, the team's, or chosen at run time), and what the generator could not settle shows as folded notes (generation_notes on the team).
  • A generated team gets a knowledge domain of its own, named from its project's slug and id, so its run notes have a domain to be offered to. Teams made from templates keep what they declare.
  • A generated team that narrates owes its script. When a role renders a recording and no role owes a script for it, a writer on the team (or a scriptwriter the generator adds before the narrator) owes one, with a spoken word band and a narrator cast, and the narrator renders from it. Generated narrating teams gain an agent and a checked script.

Changed

  • A run's review notes name every owed file that is missing. When a run finishes Completed, its review notes now also list each file a team agent owes that is not on disk at the end of the run, checked the way the step gate checks it ("Video Editor owes video/film.mp4, which is not on disk"), and re-read every page that owes links. Files an earlier run made count as present. The run's status is unchanged; the notes say why it needs review.
  • A plan holds its pages and pictures in order. A planner's page step that owes shows DIR/ now runs after the steps that fill DIR/, and when a node's declaration replaces the planner's paths, a picture glob owes at least as many files as the planner named. A step owing a 3D file (.fbx, .blend, .glb, .umap and the like), which nothing in a workspace makes, owes a written brief in its place. Plan-path results before and after are not directly comparable.
  • The deliverable gate catches stand-ins. A page whose <img>, poster, <audio> or <video> points at a file of the wrong kind fails self-contained; a 3D deliverable that is text under a 3D name fails the format check; write_file refuses text at .blend, .glb, .usdz, .3ds, .umap and .uasset paths; and shows DIR/ counts the pictures in DIR/'s subfolders. Gate results before and after are not directly comparable, for templates and generated teams alike.
  • A generated team that speaks draws with Qwen-Image too. It used to stay on SDXL.
  • The Quick Create generator drops a names code the request never writes, instead of holding a page to a code it invented.
  • An agent made with the Designer's generate gets a model from the same model policy the team generator uses, unless that policy leaves the model to be chosen at run time.
  • A re-plan turn and a send-back re-run get their own step budget. On the plan path, the supervisor's re-plan turn after a failed step, each owner's send-back repair and the checker's re-check now start a fresh step clock (logged [STEP-CLOCK]) instead of running on what the step before them left. A step that hit its step timeout is now actually re-planned: its re-plan turn used to be refused by the same expired clock. Plan-path results before and after are not directly comparable.
  • A step that wrote test files owes passing tests. A step that wrote a test file by the test runners' own conventions (code files under test/, tests/ or __tests__/, or named *.test.*, *.spec.*, test_*.py, *_test.py or *Tests.cs) and ran no tests has them run by the engine, whatever its description says, and a failure fails the step without asking the judge. A description that names node:test now counts as promising tests too. Coding results before and after are not directly comparable.
  • A failing quality check is retaken after every round of a plan step. On the plan path, a failing validate_web_app or whole-suite run_tests is taken again with the agent's own arguments after each round, before any gate reads it and whether or not the step verifier is on, so a step that started its server after its first check is no longer failed on the stale report. The retake is logged as a verifier call to the same tool. Step outcomes before and after are not directly comparable.
  • A failed run offers its notes to its team's domain. A run that failed now offers its run notes as a candidate, so what failed and why reaches the domain; it offers no files, which still come only from a completed run's steps that passed their check. A cancelled run offers nothing. The notes name the team by its template id rather than the project's generated name.
  • A single agent is held to what its node owes. In the Single topology, an agent whose node declares required_output now runs the same end-of-step gate as a Sequential or Roundtable stage, with a Sequential stage's two repair turns; an agent still owing after them fails its step and the run, where the step used to complete whatever it had left. A Single step also records the files it wrote. A node that owes nothing is not gated, and no shipped Single template owes files. Results for single-agent teams that owe files are not directly comparable before and after.
  • Quick Create gives a word-range writer edit_file. A generated role that owes a file at a word range (words 240-260) and can write files is also given edit_file, in any topology, so a length repair cuts the file instead of writing it again. Generated teams for such briefs differ from before.

Fixed

  • A build run records who launched it. Every run used to read sourceAgent "ui", template launches included. run-from-template now records the sourceAgent it was sent, and POST /api/orchestrator/run accepts an optional sourceAgent (absent: the Studio); the run's build.completed and build.failed webhooks carry the same value.
  • A planned run's step counter shows the plan step. A retried step used to advance the run's counter, so a four-step plan could read "step 3/4" while on step 2 and finish "5/5"; the counter is now the plan step being worked out of the plan's step count.
  • A Designer save keeps an agent as you edited it. Removing a tool from an agent built from a library role or from Quick Create's profiles, or setting its model to "use the team model", was undone on save, because the save rebuilt the agent from its role and profiles. An agent already on the team, or added with its tools listed (from the library, or generated), now keeps its tools and model as saved. The save still adds the wiring a team needs to run (a supervisor's delegate, every worker's reply), and a blank agent is still filled in. Teams made from templates or Quick Create are unchanged.
  • The Knowledge page's panes scroll on their own. On a wide screen the folder list, the file or document list and the reader each fill the window and scroll separately, so opening something far down a list no longer scrolls the reader away. The library has "domains" and "documentation" tabs, and a documentation folder takes two lines.
  • A pack download is recorded. Downloading a pack never wrote its date or counts to the pack registry; it now does. A download also lands beside the pack instead of over it, so a failed one no longer leaves half a pack, and a file entry that resolves outside the pack is skipped.
  • A dynamic-parallel synthesis that wrote nothing no longer passes. When the lead of a team that owes no files ends its synthesis without writing one, it is sent back up to twice, the second time told to call the tool now rather than say what it will do. If it still writes nothing, a reply of at least 800 characters is saved by the engine as the synthesis, at the last file the brief names that no subagent wrote (else research/final-report.md), with a System line saying so; otherwise the synthesis step fails with "The lead wrote no synthesis file." A research run that used to close Completed with no summary now fails.
  • validate_web_app says why nothing answered. When a check cannot connect, the report ends with the last 12 lines of the workspace's server log, where background run_shell output goes, so a server that crashed on start shows its error in the report.
  • Quick Create keeps deliverables named from the container root. A brief that names its files as /workspace/site/index.html had every owed path dropped as absolute, and the generated team went out owing nothing. Those paths are now read as workspace paths, both in what each role owes and in the check that every file the request names is owed.
  • Quick Create drops a shows <dir>/ >= 0 clause. The gate reads a zero there as one, so a page was held to showing a picture no role could make. The clause is now dropped with a warning.
  • Studio's empty plan panel no longer mentions a supervisor. While a run has no steps yet, the Plan panel reads "Steps appear here as the team starts them." for every topology.
  • A deploy no longer leaves an open browser on a blank page. The client shell at /app, and every client route it answers, is served with Cache-Control: no-cache, so a browser revalidates it and picks up the new bundle names; a missing /app/assets/* file answers 404 instead of the shell; and a tab opened before a deploy reloads once when a page it loads on demand fails to load.
  • A failed run's result card says why it failed. The run's end event (RUN_ERROR) carries the run's error message as error in its aetherium metadata, and the Studio's result card leads with it, with the agent's own summary folded below. Runs that ended before the change keep the old headline.
  • The Templates page's panes scroll on their own, so picking a template far down the list no longer scrolls the team it creates out of view. An agent added in the Designer is brought into view instead of landing off screen.
  • A plan run's step count is the plan's. A run's "step N/M" took M from the team's agent count, so a six-step plan on a seven-agent team read 6/7 when it finished.
  • The nodejs-20 pack points at Node 20's documentation, the v20.x branch, instead of the main branch. Download the pack again to replace its files.

2026-10-10

Added

  • Shared knowledge domains take documents only with the owner's approval. A domain that any template names in target_collections is shared. A completed run offers it the text files of each step that completed and passed its check, and an agent's write_knowledge into it is offered the same way. Each such document is a candidate: its own project can search it at once, and other teams cannot. The Knowledge page lists the candidates with read, approve and reject in place, and GET /api/knowledge/candidates with its approve and reject routes does the same. Approval keeps the text under Aetherium:KnowledgeIndex:KbRoot and adds it to the domain, as Domain/<domain>/<path>, for every team bound to it. Runs in benchmark mode offer nothing.
  • Each agent searches its own team's knowledge. Every node gets a knowledge scope when its session starts: the team's packs (knowledge_packs, or detected from the base image, tools and prompts when that is unset), its target_collections domains and the project. A node that runs code also gets its base image's packs, and a node holding comfyui_generate gets the image packs for the team's image_model. Knowledge is now injected into hierarchical delegations (searched once per plan step and replayed unchanged for the step's later rounds and repairs) and into dynamic-parallel subagents, besides the single, sequential, roundtable and lead turns. Excerpts are framed as reference data, not instructions. AI_KnowledgeInjection=false turns injection off, and benchmark mode always does.
  • The detailed health report covers the knowledge index. A rag block says whether the active index is in the embedding space queries use: generation, stamps, a fingerprint check, how many collections a query cannot search, and the pinned Chroma image. It never changes the overall status.
  • The knowledge index has generations. Collections are named by generation behind the Knowledge_ActiveGeneration setting, so a rebuilt index can sit beside the live one and go live, or roll back, without a restart. A collection is created only by a write, with cosine distance and a stamp of the embedding model and prefix scheme, and it refuses vectors embedded any other way.
  • The engine speaks for the bots. POST /v1/audio/speech takes OpenAI's text-to-speech request and renders it in a designed voice, chosen by name from Aetherium:Tts:RelayVoices. The whole text is one request, so the speaker never changes partway through. The voice service starts on the first request (about a minute) and stays up for Aetherium:Tts:RelayIdleMinutes after the last one, so a conversation only waits once. While a run holds the card, a request waits for it and then answers 503 with the reason.
  • Operations has a bots tab. A card for each chat client that uses the /v1 relay says where its next turn will be served from, whether its main model and its fallback are ready on each slot (and, when they are not, which run or batch job holds the card), the leases it holds and when it was last seen. Below the cards are the voice relay's state and every webhook target, from config as well as the database, with its events and an explicit off state. The host-port pool moved onto the containers tab.
  • Knowledge index rebuilds run as a checked job. POST /api/knowledge/rag/rebuild builds the next generation beside the live index from the packs on disk, the projects' knowledge folders and the old index's domain text, checks it (chunk counts, each document finding itself, title lookups inside each template's packs, fast-against-exact search agreement, the embedding fingerprint) and goes live only when every check passes. GET /api/knowledge/rag/rebuild, the health rag.build block and the Knowledge page show progress; re-indexing one pack is queued and answers 202. Pack text is cleaned of front matter, heading anchors and reStructuredText markup, and the .NET pack leaves out its standalone code samples.
  • Every shipped template names its knowledge packs, and the team designer has a pack picker. The web team reads Node, React and Tailwind; the scripting teams Python or Node; .NET maintenance .NET; the creative teams none.
  • Image teams get image-writing notes. Three image packs (general, Qwen-Image and SDXL) reach every agent that can make images, matched to the team's image model.
  • You can see what each agent read. The Studio's Knowledge panel and the run viewer list the documents each agent was given and how close a match each was, and search_knowledge results name their sources.
  • The Knowledge page shows the live index: its generation, whether its embedding stamp matches, mismatched collections, a running build, and each pack's indexed document count with a re-index button.
  • The Studio reads the way the run went. The conversation is divided into runs and plan steps; checker and run-judge verdicts show as chips (passed or failed, a score out of 10, the reason, the issues folded under them); the supervisor's plan reads as who owes which file; and pictures appear as thumbnails under the call that made them and again in the result card when the run ends.
  • An image tool card. comfyui_generate results list the pictures they saved, by workspace-relative path, and the Tool Cards gallery and the run viewer draw them.
  • A step that promises passing tests has them run. When a step's description promises passing tests (npm test, pytest, dotnet test, node --test, run_tests, or "tests … pass") and nothing in the step ran them, the engine runs them the way run_tests would, and a failure fails the step with the runner's lines as the repair note. Coding results before and after are not directly comparable.

Changed

  • write_knowledge into a shared domain reports a candidate. The receipt carries domain_candidate with the domain's name, and domain_indexed stays null until the owner approves the document. Writes to any other domain are unchanged.
  • One knowledge search path, with a configurable floor. Every search is filtered to the caller's scope before the nearest-neighbour step, keeps at most two chunks per document and scores by cosine similarity whatever the collection's space. The floor is ChromaDb:ScoreFloor (0.525, where the old hardcoded 0.05 sat), and the fallback that pulled in every pack when a project had few hits is gone. search_knowledge shows up to 1,600 characters per result, its collection argument may name a pack as well as a domain, and searching never creates a collection.
  • Embeddings use nomic's task prefixes. Queries are embedded as search_query: … and chunks as search_document: <title> with their heading breadcrumb, in batches of 32, as unit vectors. Chunks follow document structure (heading sections, code by line windows) and never exceed 2,000 characters. ChromaDb:ChunkSize, ChunkOverlap and ChunkingStrategy are removed.
  • ChromaDB is pinned and local. The compose file pins chromadb/chroma:1.5.2 by digest and publishes it on 127.0.0.1 only. An optional nftables guard (scripts/net/) keeps the Engine's port to loopback, the tailnet interface and Docker's bridges. The knowledge routes that drop collections or delete the library answer only on the Engine's own machine, and library uploads take one plain file name, text or PDF, up to 25 MB.
  • A chat turn no longer waits through a model load. While a model is actually being loaded or unloaded on a slot (the start-up pre-warm, or putting the standard set back after a run), a turn from an external chat client is refused at once, so the client's failover chain moves on instead of hanging until its own timeout. A turn behind a probe or a plan that loads nothing still waits.
  • A slot whose standard set is on its way back reads restoring, not degraded. The queue's probe, and the Inference page, say how many seconds remain before the restore, that it is running, or that a queued lease takes the card first; degraded is kept for a restore that failed or a slot nothing is restoring.
  • The Inference page says what each lease is doing. A client's lease shows how long it has been idle and when it expires, a run or batch lease how long it has been held and when its TTL ends, and a queued one how long it has waited. Leases that ended in the last ten minutes are listed with the reason, and lease holders are shown by name with the raw consumer string kept beside it.
  • SDXL images render with their checkpoint's recommended settings. A registry, Templates/composition/image-models.json, holds each checkpoint's sampler, scheduler, steps, CFG and sizes. The default checkpoint now renders with DPM++ 2M Karras, 35 steps and CFG 6.5 instead of euler, 25 steps and CFG 8: about 25 seconds an image instead of 19. SDXL image results before and after are not comparable; Qwen-Image is unchanged.
  • semantic-search takes a domain, a pack or all. A collection name that is none of them answers 404 with the valid names instead of an empty result.
  • Markdown with backslash-escaped code fences is refused, then repaired. A Markdown write whose lines open with \`\`\` is refused twice for the same file and repaired on the third try. This changes agent behaviour, so earlier documentation-writing benchmark results are not directly comparable.
  • The Studio keeps how the last run ended. The badge shows Completed, Failed or Cancelled with the run's duration instead of going back to idle, and a result card under the conversation carries the engine's summary. Failed tool calls fold to one line with the error, identical failures in a row merge with a count, and the page fits narrow windows and phones.
  • A step that completed with a failed check is amber. The step-finished event carries the verifier's verdict, so the plan reads "check failed", "unchecked" or "skipped" instead of a green success.
  • A write an engine guard refuses on purpose is "held", shown amber and counted apart from failures.
  • Agent colours drop orange, which read as a warning, and the engine's own checkers are grey.
  • Gemma-4's own reply syntax is unwrapped. When the model writes its reply as reply{content:…} text, the conversation shows the reply and the next step receives it without the wrapper. This changes what agents read in those runs.
  • Every completed run offers its notes to its team's domain. A run on a team with a shared domain offers a run-notes document each time, written from the run's own records: the brief, each plan step with its check verdict and reason, the files it made, and the run judge's score and summary. Its files are offered as well only once the domain already holds approved or salvaged knowledge; until then they go to the run's own project.
  • A failing quality check is retaken before it fails the step. A failing validate_web_app, or a whole-suite run_tests, is run again with the agent's own arguments, and a retake that passes clears it, so a server started after its first check no longer fails the step. A step whose last validation failed can now pass.
  • A step that runs out of time is re-planned. A plan step past its timeout used to end the plan; it is now re-planned like any failed step, the re-plan is told it timed out, and its re-run is told to make smaller edits. Runs with a step timeout before and after are not comparable past it.
  • A finishing send-back says what the engine's checks found. When the delivery gate fails, each step sent back is given the failing check_finishing rows for its own files, so a row the report names no page for (the favicon) still reaches the page's owner.
  • Knowledge search results name their collection and chunk, since one document can sit in a domain and a project at once.

Fixed

  • Steer reaches the agent again. A steer was accepted and then delivered to no one; it now reaches the working agent before its next turn, appears in the conversation, and is followed by a line saying which agent it reached.
  • Re-indexing a document replaces it. Document ids are SHA-256 based everywhere; four writers used a hash that changes every time the process starts, so each restart left a second copy behind.
  • Session start no longer clears the knowledge index. A team whose index read empty triggered a rebuild that deleted the global collection; session start now only logs packs a team needs that are missing.
  • A project's knowledge file stays in its project. Saving one from the library indexed it into the shared collection every team reads; it now goes to the project's own collection.
  • A detached team renders with its own image model. The finishing pass's image tool read the host project's team, so a Qwen-Image team could render through SDXL outside the service it had leased.
  • The router re-warm no longer races the arbiter's own restore. After a lease released a router slot, the check that reloads a restarted router could start the same reload during the slot's restore delay, and logged the lease's eviction as a server restart.
  • The chat relay tries the next copy of a busy model. When a model is loaded on more than one slot and the first refuses because a run or a batch job holds it, the /v1 relay now sends the request to the next slot with a resident copy instead of answering "model is not loaded". The refusal is sent only when every resident copy is busy, and a request the first slot accepts still never waits on a slow slot.
  • A team switched to SDXL starts the SDXL image service. The run started the service its template named, so a poster team changed to SDXL in the designer had every image call refused; the run now starts the image service that matches the team's image model.
  • A project's pack note lists only the packs its team searches. It used to list packs found by a fresh detection, such as pytest for a web team whose tester runs tests.
  • A failed index rebuild is never written over. Each rebuild takes a new generation number, so a build that failed its checks is left as it was.
  • run_tests reads node:test results. It reported node:test suites as "no tests ran" whether they passed or failed.
  • A chat client's lease no longer lapses mid-turn. A turn longer than the idle window could let the lease expire and a queued batch job evict the model under the client; the lease now stays open until the request's last byte, and the idle window starts when the turn ends.

2026-10-09

Added

  • Finishing Phase 2 repairs before it restyles. At its start the Engine writes finishing/repair-list.md: the build team's required outputs that fail on disk, held to the template's current conditions on the paths they already name, and every page reference that points at nothing. The director plans the repair as step 1, owing exactly those outputs. The finishing designer can now generate pictures (Qwen-Image) for it.
  • Phase 2 keeps what the build delivered. The build's checkable required outputs that still hold travel in the Phase 2 message, and the director gives each to any step that edits its file.
  • The finishing gate records what Phase 2 took away. Pages are measured at Phase 2 start, and the gate's note lists any page that then lost pictures, headings or most of its words, or was removed. Recorded, not enforced.
  • A delivery FAIL goes back to the steps that own it. A stated FAIL in the finishing delivery report sends the work back to the plan steps that last wrote the files the failing check_finishing rows name, or to the step that owes them.
  • Quick Create explains a team it cannot design. When every design attempt still owes something no node can make, POST /api/teamgenerator/generate answers 422 with the reasons as plain text, and Quick Create shows them instead of saving a team no check can hold.
  • The finishing gate notes a brief's uncaptured references. When Phase 1 hands the brief to the operator, any Reference: slug that is not a capture under finishing/references/ is written in the gate's note.
  • A terse image prompt is reported. [IMAGE-PROMPT] names a description under eight words: a subject with no setting, light or medium leaves the model to guess what it is.
  • A Finishing page. /app/finishing (Operations → Finishing) lists each project's current finishing gate, opens a pass for a named project, shows the brief and the three candidate pages, takes the answer that releases Phase 2, and shows Phase 2's repair list and delivery gate, with links to both phase runs.
  • The finishing answer is a form built from the brief. Pick one of the brief's directions (required to send), tick the slop-audit findings to fix, answer each of the brief's questions (a blank one reads "No preference; your call") and say what to keep. It is sent as plain text Phase 2 already reads; "edit as text" opens it for free editing, and a brief the page cannot read into choices gets a plain text box.
  • An image description copied from the role prompt is reported. [IMAGE-PROMPT] names a description that shares six or more words in a row with a generated role's own instructions (words the request also uses do not count): the example or the instruction sent to the image model in place of a description of the picture.
  • The finishing brief recommends a direction. The designer ends the three directions with Recommended: <A, B or C> — <why>. The Finishing page marks that direction in the answer form, and Phase 2 takes it when the answer names no direction, saying so in its plan.
  • Project cards show run state. The badge on each Projects page card said ACTIVE for every project that was not archived. It now shows RUNNING (with how long), the last run's outcome (COMPLETED, FAILED or CANCELLED) with its age, INTERRUPTED for a run a server restart killed, ARCHIVED for an archived project, and nothing for a project that never ran. The data comes from the new GET /api/orchestrator/runs/latest, each project's newest run in one request.

Changed

  • The workspace tools take file_path and text. read_file, write_file, edit_file and revert_file accept file_path as another name for path, and write_file accepts text for content. The Engine renames them when it parses the call, so the guards check the path a model wrote under either name.
  • The finishing delivery gate is owed as a PASS, and the PASS is checked. The final Phase 2 step owes finishing/delivery-gate.md verdict PASS. A PASS is held while check_finishing's own rows for the project, or its gating anti-slop rows, still fail, and no step description names a verdict: told to write PASS, a report once passed four failing rows. The gate's note carries the verdict the report states, and the ops role no longer owes the report outside that step.
  • check_finishing asks what applies to the kind of project. Its first row names the kind (app, site or documents). README and version rows apply to apps only, favicon and empty-state rows to apps and sites, and the finishing pass's own finishing/ folder is not read. The Phase 2 message names the kind, so a site is no longer given a README, run script and health endpoint.
  • A planned run is judged on each step's newest attempt. A plan step that failed and then ran again after a re-plan or a send-back no longer fails the run when the re-run passes; the failed attempt stays on record. build_steps gains PlanStepNumber.
  • In finishing Phase 2 an owed file must be written in that run. A file already on disk from the build no longer passes a Phase 2 step that never touched it.
  • The finishing team runs at a 16,384-token context, the size its director and designer model is served at.
  • A generated node that owes a web page gets 8,192 output tokens, so a page written in one call is not cut short at the team default.
  • Phase 1 states the build it finishes. Its message names the source build's status and error instead of saying the build completed, and the brief opens on that paragraph, so a pass opened by hand on a failed build no longer calls it ready.
  • A re-opened finishing gate starts clean. Before Phase 1, the earlier pass's finishing/ files, except style-record.md, move to finishing-history/ beside the workspace.
  • A repair round works on the file the gate named. The step's other deliverables that already meet their conditions are held as they are, so the round cannot rewrite what passed.
  • Quick Create's names clause lists what a reader sees, never a file's name, so a page that shows its photographs is not failed for not printing their file names.
  • The generator's Image Maker describes how each picture looks: the subject's shape, parts and materials, what surrounds it, the light, and whether it is a photograph or an illustration, written fresh for each picture instead of the brief's bare noun.
  • A checker's FAIL is asked to name the file. A checking stage or plan step whose report states FAIL but names no file another stage owes is refused once and shown those files; a FAIL that still names none fails the run as before.
  • Quick Create drops numbered placeholder names. A names clause such as Exhibit 1,…,Exhibit 4, made from a count in the request ("four exhibits"), is dropped unless the request uses those names itself.
  • The Phase 2 message gives each repair one line. Each repair-list item travels as its file and the head of its finding; the full text stays in finishing/repair-list.md, where the repair step reads it. The finishing director gets 10,240 output tokens; its workers keep 8,192.
  • Empty states are asked for only on lists filled from data. The site readiness line and the finishing designer no longer ask for an empty state under a list written into the page.
  • A failed plan step puts back the files it changed. When a step fails by its gate, its timeout or an exception, the text files it rewrote go back to what it found when it started (logged [STEP-RESTORE]). Files it created and its own verdict reports stay. A timed-out repair had left a page cut off mid-write, its photographs gone.
  • The finishing delivery gate runs last. A plan step that owes finishing/delivery-gate.md waits for every other step, whatever depends_on the plan gave it, so the gate reads the pages after the last edit.

Fixed

  • A background GPU job waits for a run on the same card. A batch lease that would start a GPU service or load a model now queues while a run holds the card, and starts when the run's lease ends. The arbiter budgeted memory only, so a finishing pass's queued 2K image re-renders were granted beside the next pass's run and its model requests timed out. A batch lease on a model already loaded (the decision layer, the trajectory judge) is still granted beside the run.
  • The asset-reference check runs again after a verifier repair. A repair that rewrote a page could leave it with broken picture or link references and the step still completed.
  • Finishing candidate pages find the project's pictures. The designer is told that a page in finishing/candidates/ reaches a project file as ../../ followed by its workspace path. First attempts had linked images/x.png, which resolves inside finishing/ and left the page's pictures dangling.
  • A quoted attribute value that contains the other quote character (src="keeper's cottage kitchen.png") is read whole by the asset, self-contained and dead-anchor checks.
  • A file that was on disk before a plan began is a file the step must write, not one it is told is already correct.
  • The default-typeface finding names every file that sets the face, not only the first.
  • A heading whose content is subsections is no longer reported as empty.
  • A plan step that runs again after failing, because a re-plan or a send-back re-pended it, owes every file it was asked for. Its own earlier attempt is no longer held up as already correct.
  • A step that displays images already in the workspace treats them as inputs: it is no longer told to write a text file in place of each picture.
  • A generated team's names clause drops a code the request uses only as a file name, glob owes included (images/si-0*.png); a code the request also uses as a word is kept.
  • The same-subject report skips a framing opener ("The image is a … photograph of") before it reads a prompt's subject, so six different photographs are no longer reported as one subject.
  • A finishing Phase 2 that fails keeps the delivery gate's stated verdict in the gate's note; before, only a completed Phase 2 carried it.
  • A broken image path in a finishing candidate mock-up (finishing/candidates/) is still logged but no longer fails the run: a mock-up is judged at the step that writes it.
  • The unread-verdict report counts the files check_finishing reports on as read, so a delivery gate built from its rows is no longer flagged.
  • A finishing gate names its Phase 2 run. Each phase's run is recorded on the gate when the run starts; Phase 2's never was (the column kept its claim token), so nothing could link to it.
  • A favicon passes only as an image the page's icon link reaches. The favicon row took any favicon.* file and any icon link, so a favicon.txt beside a link to a favicon.ico that was never written passed. It now wants an image file the link resolves to from the page; a data:image/ link or an off-machine URL, which the check cannot see, counts as reaching one. Its detail suggests favicon.svg, an icon a text tool can write.
  • A finishing run that fails before it plans closes its gate. Only a completed run moved a finishing gate on, so a run that failed before planning (no plan, a VRAM failure, an exception) left the gate in its phase until the claim was cleared by hand.
  • A verifier repair that breaks a clause fails the step. The deliverable gate was read again after the verifier's repairs only when the judge passed. A judge that still failed left the step with the gate result from before the repairs, so a page given a © in a repair round, under forbids copyright, closed Completed.
  • A dark inference slot no longer stalls the chat relay for 15 s. When a model was loaded on no slot, the /v1 relay asked each slot in turn for a loaded copy with no time limit, so one slot that never answered held every refusal for its whole probe timeout before the client's fallback chain could move on. The slots are now asked together, each within the 1.5 s /v1/models already uses.

2026-10-08

Added

  • A node with a model but no slot runs on the default node slot. Aetherium:VramOrchestration:DefaultNodeInstance (shipped as Worker): at run start such a node, and subagent_defaults, moves to that slot when its standard set holds exactly one model the node's id matches, and its model_id becomes that id. Before, it fell to Primary and was loaded beside, or in place of, the supervisor model. A model the slot does not hold, a pinned node and a node with no model are left alone; each move is logged [P4-HOME].
  • Voices can be written as descriptions. A cast entry that starts with design:, or any value of three or more words, is designed once by the qwen-tts-design GPU service (VoiceDesign, port 8101, Aetherium:Tts:DesignBaseUrl), saved as a reference clip and cloned for every line. The same description reuses the same clip; tts_status reports the design service.
  • The Team Designer edits voices and the image model. A node's voices and default voice, and the team's image model (SDXL or Qwen-Image), can be changed in the Designer; the team patch carries tts_voices, tts_default_voice and image_model.
  • A generated team's project gets its brief as project-config.md. A team made by Quick Create carries project_brief, which is written to the project root and the workspace when no config exists, so the roles told to read that file first find the brief there.
  • Runs of model-less nodes are visible to the arbiter. Such a run takes a resident-only run lease on each slot it uses: it loads and preempts nothing, a card hold waits for it, and the relay's interactive lease is refused on that slot for its duration.
  • A PASS is held to its citations. A report owing a verdict that passes the work while citing workspace files that do not exist fails its entry, naming them.
  • On the plan path, only the node that owes a verdict report may write it. Another node's write, edit or revert of that report is refused, naming the owner.
  • Reproducible team designs for scripted callers. POST /api/teamgenerator/generate takes an optional seed query parameter: every design attempt then samples at temperature 0 with that seed. Quick Create sends none and still samples at 0.3.
  • A generated team that renders speech carries its voice cast. When the team's speakers clauses name who speaks, each role holding tts_render with no cast of its own gets one (a reference voice named after the speaker first, then unused voices in name order) and a prompt line saying the cast is set.
  • Dynamic-parallel teams are held to what their nodes owe. The lead's synthesis turn is told every owed file with its conditions, a subagent whose file falls under a banded glob is held to that band while it writes, and after synthesis the stage gate runs over everything the team owes. When it fails, each owed file failing its own conditions gets one repair subagent (up to six, in parallel), the gate runs again, and the lead's one repair turn is left for what no single file explains before the synthesis step fails. A team that owes nothing runs as before.
  • The links clause. guide/index.md links guide/*.md holds an index to its set: every file the glob matches on disk must be linked from the page, and every relative link must name a file that exists (a link starting with / reads from the workspace root). Every gate reads it, and Quick Create can owe it on any index the request asks for.
  • A subagent starts when the files it reads exist. An index the team owes under a glob other subagents fill waits for them, and a subagent whose task names an earlier subagent's file waits for that one and is told which files were written before it started.
  • A plan step waits for the files it reads. A step whose description or node prompt names a file an earlier step owes is made to depend on that step (logged [PLAN-READS]), so steps the planner left parallel can be serialised.
  • Qwen-Image results suggest a reference. A Qwen picture made without reference_images ends with a sentence offering its path as <image1> for later pictures of the same subject.

  • Generated teams get voices of their own. When the qwen-tts-design GPU service is configured, Quick Create's team design describes each speaker's voice in one sentence (gender, age, pitch, pace, timbre, accent, energy). The cast is written as design: … voices, and the team declares qwen-tts-design beside qwen-tts. A description under four words, or one that repeats another speaker's, is not designed; that speaker gets a reference voice instead.

  • template_id on a team. A project created from a template records the template it came from, since the team's own name becomes the project's.
  • Anti-slop rows in check_finishing. Seventeen rows join the five it had, each named slop/<family>: <tell> [hard|purpose|lock]. They cover chatbot residue, stand-in people, unsourced numbers (a stat on a page that no research, facts or brief file carries), template FAQs, visible placeholders, generic calls to action, and three or more copy-tell families together. They also cover dead in-page links, removed focus outlines, glass and glow doses, and a default gradient or typeface with no written reason. The rest are palette size, pill-shaped everything, and banner and emoji comments. A purpose row passes when the style record or brief gives a reason.
  • Four report-only passes when a run closes. [SLOP] runs the same rows over what the run shipped. [RENDER-AUDIT] loads the entry page in a real browser: overflow and tap targets at 390 px, WCAG text contrast and visible Tab focus. [IMAGE-PROMPT] checks for template slots, quality boosters and text requested inside an image. [MEDIA] decodes each film and reports frozen and black stretches, distinct frames, silence and loudness range.
  • The finishing team runs a formal anti-slop pass. Before Phase 1 the Engine writes finishing/slop-audit.md, numbered findings ranked HIGH, MEDIUM or LOW, and hands it to the director. The brief carries it unchanged, gives each candidate direction a Design Read and ENERGY/RHYTHM/MOTION dials, and asks which numbers Phase 2 should fix. In Phase 2 the designer fixes the chosen numbers. Ops then writes finishing/delivery-gate.md with a PASS or FAIL and evidence for each check, ending on a VERDICT: line, which the gate records but does not yet enforce.
  • film_motion on a team. The film cut from the team's stills moves: each still sits on a blurred fill of itself and slowly zooms in over its share of the narration. A template that sets it has its slideshow command swapped at project creation. No shipped template sets it.
  • AI_ImageExampleArm (SystemSettings, unset by default). B removes the "8k photorealistic" booster from comfyui_generate's example prompt, so the change can be A/B tested first. Unset leaves the schema exactly as shipped.

  • A record that names a missing file is a finding. A new report-only row, slop/media: records name missing files [hard], in check_finishing and [SLOP]: a markdown file that names a full /workspace/ path to an image, clip or track the workspace does not hold.

Changed

  • The step verifier follows the operator's role placement. Seven presets (pop-culture-roast, restaurant-kitchen, space-camp-kids, local-threads-retail, fullstack-web, poster-studio, poster-pipeline) no longer pin their verifier to Secondary; it takes the Verifier entry of Aetherium:RolePlacement as a hint and can fail over.
  • Shipped gemma workers run on the Worker slot. The gemma-4-e4b-it nodes of restaurant-kitchen, space-camp-kids, fullstack-web, finishing-team and local-threads-retail are pinned to Worker instead of Secondary. A box with no Worker slot cannot run these templates as shipped.
  • tts_render checks the cast before it renders. A voice with no reference clip refuses the call once, naming each missing voice, a voice whose name differs only in case, and the voices that exist, and offers a cast that gives each speaker a different existing voice. A cast where only some voices are missing no longer renders an MP3 that drops a speaker.
  • A word band counts a document's words. Only a script (a cast clause, "script" in the path, or mostly speaker lines) counts spoken words; a review or report counts every word.
  • forbids matches whole words. A ban on cure no longer fails secure, and inflections still count. The owed-file note for a copyright ban now says it also rules out © and "all rights reserved".
  • A checker's FAIL goes to the stages its defects name. Lines marked PASS, or under a heading that reports success, no longer send their stages back; a defect on a file no stage owes goes to the last earlier stage owing a file in the same folder; the re-check reads the files the send-back made.
  • A binary file in a plan goes to the agent that can make it. A planned image, recording or video assigned to an agent without the tool for it moves to the one worker that can make it; with none or several, the plan is refused, naming who could.
  • read_file refuses binary files (images, audio, video, documents, archives) and points to list_files. Writing text to an audio, video or archive path now names tts_render, or ffmpeg through run_shell.
  • Generated Python teams install what they need at setup. When the request names pytest, or the packages its requirements.txt lists, the team installs them during container setup and its network policy is raised from Isolated to SetupOnly.
  • A generated page that carries the narration owes its transcript. It gets a transcribes clause on the team's script when the request says the page carries the narration text.
  • The GPU power-cap system unit and its performance-level drop-in are kept in the repository (scripts/system/); the owner installs them with sudo.
  • The circuit breaker's unread limits are removed. per_tool_limits, max_tool_calls_per_session, max_turns_per_step, max_tool_calls_per_step and max_session_tokens are gone from CircuitBreakerConfig, its thinking-model overrides, every shipped template and the recipes. No running check read them, so no run behaves differently; a template that still sets them loads and the keys are ignored.
  • A re-plan amends the running plan. create_plan is refused inside a re-plan turn and the supervisor is pointed at update_plan, which sets a redesigned step back to pending. A create_plan there used to supersede the running plan and strand its steps.
  • Generated teams carry no word bans of the model's own. Only a copyright ban (copyright, ©, "All rights reserved") survives in a generated forbids; what a request says must not appear goes in the role's prompt. The clause stays available to hand-written templates.
  • A generated design that owes less than the request asks for is handed back once, listing the entries it dropped and the files the request names that no role owes; the attempt kept has the fewest blocking problems, then the least missing.
  • The generator's prompt lists the tools each library role holds and says to give a library role only work its tools can do.
  • Three shipped checkers owe a verdict. menu/qa-report.md in restaurant-kitchen and shop/qa-report.md in local-threads-retail owe verdict PASS, and camp/safety-review.md in space-camp-kids owes verdict APPROVED, so a stated FAIL sends the work back instead of the run reading Completed.
  • A write-phase write to a file the agent does not owe is bounced. Once an agent's research tools are withdrawn and its owed files named, a write that lands none of them while none exists comes back naming what is still to write.
  • A checker's failing quality check goes back to the steps that own the files. On the plan path, a QA step whose own run_tests fails on files an earlier step owns re-runs those steps with the failure lines, then checks once more.
  • A word band on a glob holds for each file the glob matches, and a counted glob can carry a band (guide/*.md >= 5 words 150-250).
  • A sequential stage's lane sentence names the other stages' files and the stage that owes each, instead of calling every file it does not owe someone else's work.
  • Flash-Next on the primary router decodes with its MTP draft (spec-type draft-mtp, spec-draft-n-max 3): decode measured 27.8 → 45.5 tok/s at 40K context and an agent's next turn 28.2 → 21.0 s with byte-identical output, while a cold 28K-token prefill is slower (66.7 → 75.0 s).

  • Designed voices belong to a template and speak their own character's lines. A template's designed voices are kept under designed/<template id>/, so two templates never share a voice and every project made from one template keeps its cast. The reference clip a voice is designed from speaks that character's first line of 12 to 40 words instead of one fixed sample, and only voices the script actually speaks are designed.

  • Three anti-slop rows fail a run. A Completed run moves to Failed, with a "post-run audit: anti-slop: ..." reason, when what it shipped has a dead in-page link or an unsourced number, or when its rendered entry page has text under 3:1 contrast. check_finishing now returns a verdict, so a step is sent back over those rows only. Design mock-ups under candidates/ never fail a run. Rendered contrast abstains when a cross-origin script or stylesheet failed to load, when the page is still loading, or when two readings disagree. Pass rates from before and after this change are not comparable.
  • Image teams draw with Qwen-Image. The four shipped templates that make images (poster-pipeline, poster-studio, restaurant-kitchen, steam-frame-vr) set image_model: "qwen", and a generated team that holds comfyui_generate gets it too, unless it also renders speech (the two GPU services do not fit the primary slot together). On the same prompts and seeds Qwen-Image fit 6 of 9 as-written prompts and SDXL 1 of 9, at about 28 s against 18 s a picture. Existing projects keep their saved image model.

  • The poster image artist owes its pictures. In poster-studio and poster-pipeline the image node owes poster/images.md and three distinct PNGs (poster/*.png >= 3), so a record of pictures that were never drawn fails the step.

  • The menu card owes the photographs. In restaurant-kitchen the card writer owes menu/menu.html self-contained shows menu/images/ >= 10 and has an 8192-token output budget; the food stylist owes ten photographs in menu/images/ and no longer the manifest the Engine writes.
  • Agents never write the Engine's image records. write_file, edit_file and revert_file on images-manifest.md or under .image-meta/ are refused and change nothing, and a generated node no longer owes either.
  • A generated team that makes images and also speaks is cast from the reference voices, as it already draws with SDXL: designed voices beside both image and speech services do not fit Primary's budget.
  • GET /v1/models asks every slot at once and leaves out a slot that has not answered in 1.5 s; with one slot unreachable the listing had taken 15 s.

Fixed

  • The voice design service starts. The 1.7B VoiceDesign model was given the 0.6B voice service's memory budget, left no room for its KV cache, and refused to start. It now runs from its own deploy files (deploy-32gb-design.yaml, deploy-16gb-design.yaml), picked automatically for qwen-tts-design.
  • run_tests detects a pytest suite with no config file, reports a pytest collection error as a failed suite instead of "no tests found", and reads its counts from the runner's summary line (a suite with eight failures had read as one passed, one failed).
  • A bold title line in a script is no longer read as a speaker, so it is neither spoken in a voice of its own nor counted.
  • A tool call that survives a cut-off round with no arguments gets the split-the-write guidance instead of executing an empty write.
  • An image saved under ComfyUI's own file name no longer replaces an earlier image of the same name.
  • In a generated team, a count on a file the request names keeps the path, a page's shows clause names the folder the team's pictures go to, and the default copyright ban joins the page's forbids list instead of a transcribes list after it.
  • An empty <img src> in a page with no script is reported as a dangling reference.
  • A VRAM plan refusal names the resident models it counted.
  • A verdict is read only from the word "verdict" followed by a colon, a dash or the end of a heading line: an "Activity verdicts" table or "the verdict is final" no longer reads as one, and a backticked value parses.
  • A placeholder path in a report (images/mr-XX.png) is no longer read as a cited file that does not exist.
  • A counted glob with a word band reads both its count and its glob; the path had read as guide/*.md >= 5 and the count as 1.
  • A plan step left behind by the plan loop's limit after a failed attempt is marked failed with the reason instead of pending.
  • Sequential, single and dynamic-parallel prompts are no longer re-wrapped by the prompt enhancer on every message; a sequential stage's prompt had carried its context twice.
  • The finishing brief is written. Phase 1's finishing/brief.md is now the designer's planned last step: the director plans once and gets no later turn, so a brief it was told to write itself was never written, and the pass never reached Phase 2.
  • A plan step that a re-plan sets back to pending runs again on the wave path. It had kept its claim from its first wave, so no later wave picked it up.

2026-10-07

Added

  • Any number of inference slots. Every key under Aetherium:LMStudio:Instances other than Primary and Secondary is now a slot of its own (a key starting with _ stays hidden). Health probes, model discovery, pre-warm, failover and smart routing cover every slot, trying alternates in Aetherium:LMStudio:SlotOrder; a registry entry can take a slot out of service (Enabled: false) and seed its concurrency (MaxConcurrentRequests). The runtime row keeps Primary and Secondary under their fixed keys and holds every other slot under instances, built from the registry. With two slots every routing decision is the one made before.
  • Side roles can be placed on a slot. Aetherium:RolePlacement names the slot, and optionally a binding pin, for model calls that belong to no team node: the step verifier, the trajectory judge, compaction, summaries, lessons, code_review, Test Drive, the decision layer, session fork, model discovery, the team generator, the image prompt enhancer and embeddings. A role it does not name keeps its built-in default; a template's own verifier or judge instance still wins.
  • Run overrides place a team across slots. TeamRunOverrides gains nodeInstances (per node), subagentInstance (dynamic-parallel children) and verifierInstance (the step verifier, and the trajectory judge unless the team pins its own), each a pin. Template create, run-from-template and the resolve preview answer 400 naming the known slots when an override names a slot that does not exist, where a misspelt slot used to start a run whose tasks then failed.
  • GPU cards and card pinning. Aetherium:Gpus:Cards names the host's physical cards by ROCm UUID (and Vulkan device id); a slot names its Card, the systemd Unit that serves it and that unit's build (GpuApi: Hip or Vulkan). Every GPU unit — the llama-server routers and the leased ComfyUI and voice services — now requires a card-pin environment file written by scripts/render-gpu-env.py and will not start without one, so no unit runs on whichever device is enumerated first. The planner prices a load for the card its slot is on (ModelCostsByCard, HeadroomGBByCard), and a GPU service can carry per-card environment (Env, EnvByCard).
  • A second local router. llama-server-worker.service (port 8091, presets in llama/models-worker.ini) serves a slot on a second local card. deploy.sh installs it and leaves enabling it to the operator.
  • Per-card telemetry. GET /api/inference/cards reports each GPU as sysfs sees it — VRAM against the planner's budget, temperatures, power, fan, link, connected displays and every process holding memory on it — plus host RAM; GET /api/inference/queue carries the same. The Inference page groups slots under the card they run on, adds a host-RAM bar, and shows why a slot's probe failed.
  • Card holds. A lease request that names a card reserves a whole card for something outside the Engine: it waits for any run on the card to finish, then empties the card's slots, and while it is live nothing is granted, loaded or restored there. Releasing it restores the standard sets.
  • A restarted router is re-warmed. A llama-server slot that answers empty on two checks 30 s apart, with no lease and nothing in flight, gets its standard set loaded again. MaintenanceMode suspends this; take a lease to keep a router empty.
  • A transcribes clause for owed pages. "site/listen.html transcribes audio/scripts/s1.md,…" fails a page whose visible text lacks the spoken lines of the listed scripts (case, punctuation and markup ignored; one line in ten may be missing), naming the first missing line or a script that is not on disk.
  • An optional restore delay per slot. Aetherium:LMStudio:Instances:<slot>:RestoreDelaySeconds (default 0, restore at once) waits that long after a slot's last lease ends before restoring its standard set, restarted by every release, so a back-to-back run finds the card as it was left.
  • Voices per recording. A tts_voices key may name a recording — its path, file name or stem — and that voice then reads the file being rendered in place of the node's speaker keys.
  • Media roles in the Quick Create catalogue. The generator is offered a narrator, an image-maker and a qa-reporter, where every media role used to be written from scratch.
  • A write to an owed file's name in the wrong folder is bounced. write_file or edit_file to a file carrying an owed file's name outside its owed path is refused, naming the owed path (twice per name, then allowed).

Changed

  • Embedding requests name their model. Every embedding request and the availability canary send Aetherium:Embeddings:ModelId (default text-embedding-nomic-embed-text-v1.5) instead of a fixed placeholder name, which a llama-server router rejects.
  • deploy.sh checks GPU pins before it changes anything. After publishing it validates that the config can pin every GPU unit to a card, exiting 6 with nothing changed when it cannot; it then renders the pin files and restarts each router only when its unit, presets or pin changed. A deploy now needs Aetherium:Gpus:Cards and the Primary router slot's Card and Unit configured.
  • The relay goes where the model already is. An OpenAI-compatible chat request goes to the first slot, in slot order, where the model is resident, before one that only lists it.
  • subagent_defaults.preferred_instance is honoured. It was declared and never read; it now pins dynamic-parallel children to that slot, and their model is made resident there by the run's lease.
  • The voice service runs the Engine's own launcher. qwen-tts.service starts services/qwen-tts/serve.sh with a deploy file per card size, published with the Engine.
  • Shipped llama-server presets. The primary router's large mixture-of-experts preset uses a 2048-token batch and micro-batch (a 25k-token cold prompt measured 78.6 s → 48.8 s), and both routers cap their host-RAM prompt cache at 8 GiB (was 16).
  • Quick Create keeps the best of three designs. A design that cannot deliver is handed back with one fix per node, for up to three attempts, and the attempt with the fewest blocking problems is kept, where the last attempt used to win even when the hand-back made it worse.
  • Generated teams owe what the request names. Where the request names a file under a folder and the design owes that name elsewhere, the request's path is owed; a deliverable the request names that no role owes goes to the role owing files of the same folder and extension, with the clauses they share; and an owed entry with one clause outside the gate grammar keeps its path and its readable clauses instead of being dropped. Inputs, URLs and product names in the request are no longer taken for deliverables.
  • Every generated role carries the whole request. Custom roles now get it too, and a long request keeps its first 1,000 and last 500 characters rather than being cut at 1,500. Every owed .html forbids copyright unless the request asks for such a line.
  • Generated media roles get production guidance. A role owing images by exact name is told to make each one under its owed name; a role that renders recordings is told to pass each recording's narrator voice on every render, re-renders included.
  • A generated film's soundtrack is the recording the request names for it, else the one sharing the film's file stem, where the first recording owed by any role was used.
  • The repetition rule scores failing-write cycles. Its outcome watch now covers a fire on failed writes cycling on one target (rule version v5); outcome rows before it are not comparable.

Fixed

  • edit_file with a start_line and no end_line searches to the end of the file. It searched that one line, so a fix named by a gate ("delete those exact words") missed text further down the page. An empty search still treats the omitted end as start_line.
  • A hung inference server no longer stalls the queue and the relay. Read-only slot listings give up after Aetherium:LMStudio:ProbeTimeoutSeconds (15 s) instead of the 120 s load timeout, and the queue probes its slots in parallel.
  • The Codestral judge preset has a chat template. Its GGUF carries none, and the fallback format left the model unable to end its turn.
  • The per-message prompt refresh no longer cuts a role's own sections. In Hierarchical and Roundtable teams everything after a role's first ## heading of its own was dropped from the first user message on.
  • An agent owing audio, video or images is no longer ordered to write_file them. Recovery and streak orders skip a binary already on disk, keep the tool that makes it, and demand nothing an agent cannot produce.
  • A checker over media can reach a verdict. Listing a media file's folder counts as opening it, and the checker is told each owed media file's size, duration and sound track as measured.
  • A shell may assemble a video. A plan step that asks an agent holding run_shell to cut, merge or assemble video, audio or an archive is no longer rewritten into a text deliverable.
  • A run releases every lease before any slot is restored, so one slot is no longer held leased through another's reload, refusing chat clients there.
  • GPU services start for a team whose nodes name no model. Its image and voice services were never started and no service lease was taken.
  • Per-call usage events have no gaps. The reload-retry path now reports usage, and trajectory judge and code_review calls carry the agent and project they belong to.
  • The Inference page no longer over-counts a process's VRAM. A client's figure is capped at its own allocation, since its resident figure counts buffers it shares.

2026-10-01

Added

  • Qwen-Image 2.1 as a second image model, chosen per team. A team whose config sets image_model: "qwen" renders on Qwen-Image instead of SDXL, through its own leased GPU service (comfyui-qwen). For those teams comfyui_generate takes ratio, transparent and reference_images, and records each image's request beside it for the finishing pass. Other teams see no change.
  • A finishing pass for Qwen-Image pictures. After every Completed run of a Qwen-Image team, the Engine re-renders that run's images from their recorded requests — a prompt-enhancer rewrite, then a 2K render — into final/ beside the originals, keeping each picture's aspect ratio. Transparent images and images built from references are skipped. The new finish_image tool, held by the finishing team's Designer, queues a slower upscale of chosen pictures. Both run after the run, under their own batch leases, and are configured under Aetherium:ImageFinishing.
  • A checker's FAIL sends the work back. When a checking stage or plan step ends on VERDICT: FAIL, the stages or steps that own the files its report names are re-run once with the report's lines about those files, and the checker checks again. A FAIL that names no owned file, or survives the send-back, fails the run with the report. The verdict clause no longer coaches toward PASS: a stated FAIL is a result, and only a report with no verdict is a defect. A checker writes only its own report — a fix to another stage's file is refused — and is held until it has opened the files it checks.
  • The deterministic repetition rule. A guard that recognises expensive or plainly stuck repetition — identical writes, failing writes cycling on one path, a call repeated with nothing changed in between — and records each fire as a [REP-RULE] log line and a trajectory row. Read-only calls never fire. AI_RepetitionRuleArmed (default off) lets it bounce the agent, at most twice per loop.
  • A decision layer, off by default. A seam for asking a resident model a typed question mid-run (Aetherium:Decision:Enabled, with a Shadow mode that records without acting), every answer written to a new decision_logs table. Its first question is whether a looping agent is repeating work under different wording. The decision's wall time is not charged to the step clock, and a model that stops answering is no longer asked.
  • A named interactive copy on an instance. InteractiveCopies maps a model to a second loaded copy; the OpenAI-compatible relay sends chat clients' requests for that model to the copy, so a long conversation keeps its prompt cache through Engine work, and falls back to the base model when the copy is not loaded. The planner budgets the copy and never evicts it.
  • Real token use per call. aetherium.llm.usage reports each model call's prompt and completion tokens as the inference server counted them, beside the Engine's estimate; prompt, usage and compaction events now name the agent and build step doing the work.
  • A run you can read in the client. The run ledger groups by agent (the default), by step or chronologically, shows real token totals, and keeps several runs on one project stream apart. Studio gains a run rail; the Live page an agent strip.

Changed

  • A sequential stage writes only its own lane. A stage's write_file, edit_file or revert_file on a file another stage owes is refused, naming the stage that owns it; a fourth attempt ends the turn. A site stage had rewritten the narration script after it was recorded, and the run shipped a recording of a script that no longer existed.
  • Sequential stages get two repair turns after failing their end-of-stage gate (was one), and each stage's prompt now lists the files it owes and is checked against.
  • Stage gates enforce content clauses. The sequential and roundtable end-of-stage gates apply every clause the plan path does — forbids, self-contained, shows, names, words, speakers, lasting — where before they checked only that files existed. A relative link in a page resolves from the page's own folder, as a browser does; a dangling one is told the path that would work from that page.
  • Tool-loop compaction keeps the work. The trigger starts from the server's measured prompt size, the verbatim tail is sized by budget rather than fixed at two messages, and the summary is a sectioned working record (task, files written and read, decisions, errors, what is next) about a third the size of what it replaces.
  • A context-pool collision waits instead of shrinking the prompt. When a request that fits its window is rejected because parallel slots share one context pool, the Engine waits for an in-flight request to finish and retries it unchanged once.
  • Generated teams. Only a hierarchical team has a supervisor: a sequential team's stages are peers, and none is granted delegate. Generated teams run at temperature 0.1, give each unmapped speaker a distinct voice, rename nodes that share a display name, and grant list_files to a node owing a page that shows a folder. A forbids copyright ban also matches © and "All rights reserved".
  • Prompts name only tools the node holds. Only a node with run_shell is told to run its code with it, and a per-model prompt fragment drops lines naming a tool the node lacks.

Fixed

  • comfyui_generate accepts the description as prompt when workflow is absent, and a call with neither is told to put it in workflow. Generated image roles were told to write "the comfyui_generate prompt", sent prompt, and every such call failed.
  • A cancelled run stops. The work already inside the session is told, and checks it every tool-loop round and before each stage, step, wave and send-back; a cancelled run could keep calling tools against a service its released lease had stopped.
  • The Knowledge Base reader opens files. Reading a global knowledge file answered 500 for every file.
  • The Knowledge Base banner counted chunks as documents and called ChromaDB an embedding model; it now shows documents and chunks separately and names the vector store.
  • A write whose path is a tool's name is refused. write_file with the path run_shell had answered success and left a file of that name.
  • A generated checker keeps its verdict clause. A QA role owing verdict PASS was dropped as unreadable, and its stage with it, so generated teams came back with no checker.
  • A banned token is named as the file spells it — "contains copyright (written in the file as &copy;)" — so the agent can find and remove it.
  • The shrink guard lets a stage that owes a file replace another agent's draft of it, and lets a checker rewrite its own verdict report.
  • Dashboard. The request count read 0 beside thousands of tokens, the hourly activity line stayed flat through a day of runs, and a failed tool card recorded its title rather than its text as the error.

2026-09-21

Added

  • A media tool call aimed outside the owed directory is refused before the generation is spent. comfyui_generate with a save_dir, or tts_render with an output_path, that would land outside what the step owes of that format is bounced at the first call with the directory or path to use. An image step had spent six generations on a directory the planner's prose named while the role owed another, and then had no tool that could move them.
  • A generated role that owes a film and holds a shell is handed the cutting command. The one worked ffmpeg concat command the shipped film templates carry, with the team's own paths, and the instruction to run it as one shell call and repair rather than rewrite. A generated editor had spent twenty minutes writing and rewriting a script it ran once, and delivered nothing.
  • A Quick Create role's owes may carry the gate's clauses — images/*.png >= 6, video/film.mp4 lasting >= 50s with audio, site/index.html self-contained shows images/ >= 5 names … forbids … — validated clause by clause; a condition the gate cannot read is dropped with a warning. A generated film cutter owing a bare path had delivered a 0.08 s clip and the gate took it: the brief's "at least 50 seconds with the audio track" had reached nobody. In a sequential team every stage must owe a file; one that still owes nothing after the generator's retry is dropped rather than run. A quoted stage number ("stage": "1") is read as a number instead of refusing the design.
  • An owed directory can name a format. images/*.png >= 6 is satisfied only by six real PNGs of distinct content; a bare images/ >= 6 was satisfied by six text placeholders written "solely to meet the quota" by a role that could not make a photograph. Empty and mis-declared files no longer count toward any directory obligation, and a file written under the directory in another format no longer counts as delivering it.
  • A sequential stage gets one repair turn. A stage that owes files and did not leave them is re-run once with the gate's own sentence — which names the missing path and what was written instead — before the pipeline aborts. A stage had rendered a correct four-minute narration to the wrong file name and the run died on it.
  • The team generator is offered only roles that name no workspace path, and a design that owes a file to a role without the tool that makes its format (an image, a recording, a video, or a page or report without write_file) is handed back to the model once with that report. A generated photographer had been the menu-photography role from another template, which read that template's menu three times and never shot anything; a generated narrator had been a podcast role and wrote a two-host podcast.
  • Quick Create teams say what each role owes. The team generator's design now carries, for every role, the workspace-relative files it must leave behind ("images/ >= 6", "site/index.html"); they become the node's required outputs and switch the team's required-output gate on. Before this a generated team owed nothing, and a step that wrote no file still completed. In a sequential team each role also carries a stage, so the pipeline order no longer depends on which of the generator's two lists a role was written in; a custom role written under a library role's id replaces it.
  • Generated media teams are runnable as generated. An audio tool profile (the voice tools) joins the library; gpu_services is derived from the tools a team holds when it declares none (comfyui_* → comfyui, tts_* → qwen-tts); every node of such a team runs on the model policy's new media slot, because the service holds most of the card; and a team that owes a video runs on the media base image, which has ffmpeg. A role that owes an image or a recording without the tool that makes one is reported rather than silently widened.
  • shows DIR/ >= N clause on a required output ("site/index.html self-contained shows images/ >= 3"). At least N distinct real files under the named directory must be referenced by the page, and the references must resolve from the page's own directory. A run could generate every asset correctly and publish a page carrying none of them: one completed run produced six photographs and shipped a two-page site with no <img> at all, its only reference a poster= that did not resolve, and every other clause on those pages passed. A reference that names one of the run's files but does not resolve is reported as broken rather than absent.
  • Rendered audio is checked against its script. No clause and no syntax: tts_render already writes a <output>.render.json sidecar naming the script it read and the spoken counts it rendered from, so whenever a step owes that audio the Engine re-parses the script and fails the step when the counts no longer match. A narrator that rewrote its script to satisfy a content clause, after rendering, shipped a film narrating a version of the script that no longer existed — the script clause read the script, the duration clause read the audio, and nothing joined them. Audio without a sidecar makes no claim and is not judged.
  • An owed glob can prescribe a file-name prefix. "images/el-*.png >= 6" counts only files whose names begin that way, so a config that names every file can be enforced rather than merely stated. A run produced six correct photographs under names of its own invention, satisfied images/*.png >= 6, and nothing but its own QA report noticed.
  • A generated role that owes images is told what to photograph. It is instructed to read the project's configuration first and prompt for the subject recorded there. The subject reaches the image tool only through the prompt the role writes, and a role told merely to generate six images produced six competent photographs of the wrong thing.

Fixed

  • The film-cutting section is placed where prompt enhancement keeps it. Enhancement runs again at run time and rebuilds a role's prompt from its "## Your Role" text; a section appended after the guidelines never reached a worker. It now sits inside the role text under a lower heading, and the converter's test drives it through enhancement twice.
  • A film step is told, in the step itself, to run the cutting command its prompt carries. The prompt section alone was not followed: an editor read "assemble the final video" in its step and composed four ffmpeg filtergraphs of its own, none of which ran. The plan contract now appends the instruction to any step that owes a video from a role that holds the command.
  • A generated media team gets a circuit breaker sized for media steps, and a step missing a directory obligation is told where files of that format sit instead. A generated image step hit the recipe's 300 s step timeout with five of six photographs shot, under a directory the planner's description had named rather than the one the role owed.
  • A step that owes a directory or a glob is verified on the files under it. The verifier's on-disk walk read owed paths one file at a time, so a step owing images/*.png >= 6 that made six real photographs was judged "No files to verify for this step" and failed as inconclusive. The obligation is now expanded to the real deliverables beneath it (capped at 24) and the judge is told each one exists.
  • A step told it is missing a file is also told what sits beside it. The gate's sentence now adds "On disk, video/ holds emberline.mp4, …" when the owed file's directory has other files in it, so a film cut under the wrong name is repaired by a rename rather than met with a fourth completion report; shell-written files were never in the step's write list.
  • A step whose only deliverable is binary is judged on it, not failed for lack of evidence. Making media steps verifiable exposed a guard that counted only text evidence: a step that produced a correct 1.4 MB photograph and nothing else was failed as having produced nothing.
  • self-contained was silently dropped whenever a later clause followed it. Its parser spelled out the clauses it had to stop before instead of referring to the shared list, so any clause added afterwards made it match nothing: "video/x.mp4 self-contained lasting >= 50s" carried no self-contained obligation at all.
  • A page reference now includes poster and srcset. Both were invisible, so a <video poster="…"> pointing at a file that does not resolve passed the self-contained check.
  • Media steps are verified. A step that owes a required output is now judged whether or not it wrote a file through write_file. Because only write_file and edit_file counted, every step whose product came from image generation, speech rendering or a shell command was skipped by the verifier entirely.
  • The verifier is told that the deliverable gate already passed, and which condition each file met. The judge only runs once that gate has passed, so its prompt now states every owed deliverable as checked — playing for at least 50 seconds, with a sound track — and tells it not to fail a step for a deliverable that looks missing or misnamed. It had failed a film that was on disk under the owed name, three times, on the strength of an earlier name in the worker's own transcript.
  • A file too long for the verifier's prompt keeps its end. The excerpt now carries the head and the tail with the omission named in the middle, instead of stopping at the budget. A page's last section and its closing tags are at its end, so cutting there made every long deliverable read as incomplete: an 8,831-character page holding all five of its subjects was failed three times for omitting the fifth.
  • A generated media team runs its plan serially. A parallel wave had started the film step's ffmpeg against an images directory the imaging step had not filled yet.
  • A deliverable that reaches a verdict is held to it. New clause qa/report.md verdict PASS: the step fails when the file states a different verdict, and when it states none, because a report that reaches no conclusion certified nothing. It is the only clause that can fail a step for work that is wrong rather than missing. A QA report had found five invented names and five invented dates, written OVERALL VERDICT: FAIL, and the run finished Completed — the node owed only that the word VERDICT appear. Decoration is ignored and the last verdict wins; the repair message asks for the work to be fixed and re-checked, never for the verdict line to be changed. Quick Create puts the clause on any role whose job is to check the others.

2026-09-20

Added

  • A deliverable must have bytes in it. Every required-output path was checked with an existence test alone, which is the weakest assertion available: a zero-byte .png and a .png holding the words "Placeholder content" both satisfied it, both recorded the step Completed, and both were congratulated in the tool loop as work finished. A path now also has to be non-empty, and for an extension whose format is self-identifying (PNG, JPEG, GIF, BMP, PDF, ZIP, MP3, MP4/M4A/MOV, WAV, WebP, AVI, Ogg, FLAC) its first bytes have to say so. Quality, length and subject stay where they belong, in the clause grammar; an unreadable file is still never held against a step.
  • lasting >= Ns with audio clause on a required output ("video/film.mp4 lasting >= 90s with audio"). The only question the grammar could ask about a video was whether the path existed, so a film of six stills at one frame each — under a second, against a 109-second narration — passed its gate and every verifier verdict. The clause reads the video stream's duration rather than the container's, because the two disagree on precisely this defect, and with audio additionally requires a sound track. A one-second slideshow now fails the step that built it.
  • filename argument on comfyui_generate. An image can be asked for by the name it is owed instead of being generated under a counter name and renamed afterwards. A directory part is discarded, a name without an extension inherits the source's, and only the first image of a multi-image call takes it.

Changed

  • comfyui_generate and comfyui_status describe themselves accurately. The catalog entry said comfyui_generate "returns a job id you can poll with comfyui_status"; it has returned the saved workspace path since 2026-09-10. comfyui_status, where that line sent agents, was still printing .NET type names for its status and outputs — the 2026-09-10 fix never reached it. Both corrected.
  • An entry whose only clause is forbids is owed again. The start-of-step split that marks an already-correct file as an assertion rather than a deliverable never tested the forbids clause, so such an entry counted as satisfied before anything had been written.

2026-09-19

Added

  • names A,B,C clause on a required output ("kiosk/index.html self-contained names KD-1991.004,The Brackwater Astrolabe"). Every listed string must appear in the file, compared case-insensitively. Every other clause asks whether a file resolves, how long it is or who speaks in it; none of them could ask whether it is about the right thing, and a published page naming none of the objects it exists to present passed every gate. Composes with the other clauses in either order and is inert where nothing can read the file.
  • AI_TrajectoryJudgeModelId, and trajectory_judge_model_id / trajectory_judge_preferred_instance in a template's verification block. The end-of-run trajectory judge read the global setting alone, so a team that pinned its step verifier to one model had its trajectory judged by another; the pin now governs both, and the trajectory judge follows the step verifier's pin when it names no model of its own.

Changed

  • A word range carries a ten-word grace either side. The band still steers the writing and a writer that misses is still told the real target; a near miss no longer costs the step.
  • A template's plan_steps description is what the worker reads. The planner's prose lends a description only where the entry leaves one out. Until now the planner's won, so an author's wording — the paths, the length, the cast — usually never reached the worker, and a planner could hand a step an instruction the template contradicts.
  • A step's declarations are split into deliverables and assertions. An entry already on disk and already meeting its clause is stated as already correct, do not rewrite, kept out of the write orders, and a write_file to it is refused. The end-of-step gate still checks it, so re-declaring a finished file remains the way to catch a later step damaging it.
  • A length failure asks for an edit, not a rewrite, and no longer ends by telling the writer to write the file again; a rewrite came back at the model's own natural length every time.
  • The repair advice for a script that parses to no speech names one line format, not three.

Fixed

  • The deliverable gate runs again after a verifier repair. It ran once, before the verifier's repair loop, so a repair could break a word range, a cast or a self-contained page with nothing re-checking it: a script passed at 196 spoken words, was rewritten to 305 by a repair, and the step completed.
  • The tool loop measures the clause, not the filename. When every owed path exists it now runs the step's own gate and bounces with the gate's wording instead of declaring the task complete, force-exiting the agent, and failing it afterwards for something it still held the tools to fix.
  • An owed path that is on disk is not missing, whichever tool produced it. A file written by tts_render or comfyui_generate was invisible to the loop, which went on demanding it while recovery stripped away the only tool that could make it.
  • A read that misses says where the file actually is. A worker that prefixes its own role directory onto a path it was given plainly used to get four words back and carry on without the file.
  • Three script shapes are read as speech: a bold-wrapped label (**[GUIDE]**), a speaker with the colon inside the bracket ([GUIDE: …]), and two speakers in one paragraph, which previously became one and made the cast gate report a speaker missing over text where it plainly speaks.
  • A words-gate failure names a voice the cast does not have, so words counted against a speaker the template never declared stop being an unexplainable number.
  • A link hint keeps its query or fragment. The self-contained check strips them before resolving a path, correctly, and then built its suggestion from the stripped path — telling a page that linked labels.html#KD-1991.004 to write ../labels.html, which meant deleting the anchor it had just added.

2026-09-18

Fixed

  • A script owed at a words range is no longer refused by the write_file shrink guard. When the required-output gate asked for a cut, the guard refused the shorter draft and told the writer to carry every section forward, so the writer put the long draft back and the step failed. The gate governs the length of such a file; the guard now stands aside for that path only, and still protects a page owed beside it.
  • edit_file refuses a single-match splice. A search of one line (typically the title) with a replace that opens the way the file opens and runs to more than four times the lines of the search used to succeed and leave the file holding the document twice. It is now refused with the whole-file form, the same as the range form has been.

2026-09-17

Added

  • self-contained clause on a required output ("site/index.html self-contained"). The gate refuses the step while the page loads anything from off the machine, pulls in an external stylesheet or script, or links a file that does not resolve from the page's own directory, naming the corrected path when the file exists at the workspace root.
  • top_p, top_k, min_p and repeat_penalty per team config and per node. Sent as top-level request fields to either backend; a node's value wins and an unset field leaves the backend's own default, which differed between the two backends (repeat penalty off on one, 1.1 on the other).
  • reasoning_effort: "none" on a team's config turns gemma-4 thinking off. gemma-4 reasons by default and the only switch that stops it, on LM Studio and llama-server alike, is a top-level reasoning_effort of none; the Engine now sends it for a template that asks. Omitted or off still sends nothing.
  • judge_max_tokens on a template's verification block (default 4096, clamped 1024 to 16384). A 12b judge reasoned past the 4096 cap before writing its verdict and the step failed as inconclusive; templates that judge with the 12b now set 8192.
  • plan_steps: a template may declare its plan. One step per entry in that order, assigned to the named agent, serial, with the node's required outputs; the planner only lends descriptions. Two planners had reordered an explicit numbered list and invented a read-only first step.
  • A template's verification block with enabled: false opts the team out. The global switch now applies only to teams with no block, as the documentation already said.
  • A Run lease may evict Secondary's protected standard set when it pins a model there; the release restores the set, and a standard-set model counts as the Engine's whichever process loaded it. Interactive and Batch leases still hold against the set. Temporary until more capacity.

Changed

  • The words gate names the direction and size of a miss ("141 words OVER, cut about 161") instead of "cut or extend".
  • The step verifier's default judge budget is 180 s (was 90 s); a template's judge_timeout_seconds still overrides it and the floor stays 30 s. Every inconclusive verdict seen on 2026-09-17 was a real timeout of a judge that needed 60 to 90 s.

Fixed

  • The asset-reference gate tells the worker how to clear it: write the file at that path or remove the reference, and remove an image or font it cannot produce. A designer had answered the bare finding by adding four more references to files that do not exist.
  • A verifier repair round keeps the step's required outputs. The bounced worker had run with no required output at all because the repair text names no path.
  • A step that owes a deliverable is always verified. The verifier had skipped a step whose planner description began with "Review", although the team contract had given it a script to write.
  • A bold speaker name with the colon outside the bold (**PIXEL**: words) is a spoken line. The parser had read that shape as prose and counted zero spoken words.
  • The step verifier is told about owed deliverables that no write_file produced. A binary deliverable such as a rendered audio file is stated with its size and marked as existing, and an owed text file is read as evidence; the judge had failed a step for a missing audio file that was on disk.
  • A refused or failed write no longer counts as a delivered required output. The loop had ended the writer's turn with "written successfully" on a draft the shrink guard had just refused, and the length gate then failed the unchanged file again.
  • A judge whose model is resident on no slot goes to the slot whose standard set names it. With a run pinned to another model on that slot, the judge had asked the other slot, which has no such preset, and every post-run trajectory evaluation failed at the lease.
  • A leading timecode on a radio-script line is stripped before parsing. A script written as [00:02] Narrator: words had counted as zero spoken words because the line began like a cue; [00:02], (0:15) and 00:25 - are now removed from the front of a line first.
  • The path guard yields to a write on a step's required output, and the judge is told the prompt's truncation marker is the harness's cut, not the file's.

2026-09-16

Added

  • A required output may name who speaks: "path words 420-500 speakers PIXEL,BYTE". Each named label must have at least one spoken line; a script with one invented narrator, inside the word window and at the right path, had passed every gate.
  • judge_timeout_seconds on a template's verification block. The judge call's budget from dispatch, default 90 s, floor 30 s. A 12b judge on the second card reading seven files had timed out at the fixed 90 s, and an inconclusive verdict fails the step.
  • tts_voices on a node, and an idempotent tts_render. A template may declare the cast on the audio node; the tool reads it when the model omits the voices argument and says in its result where the voices came from. A repeat render of an unchanged script and cast answers from a sidecar beside the MP3 instead of rendering again. One run had rendered the same 483 words six times, five of them in the default voice because the map was dropped after the first call.
  • Voice-over as a node's deliverable: tts_render and tts_status. A new audio server group. tts_render speaks a workspace script file line by line in the reference voice mapped to each speaker, joins the lines and writes one MP3 into the workspace; tts_status reports the voice service and the reference voices. The service is the qwen-tts GPU service, started by the run's lease when the template declares it in gpu_services. Configuration under Aetherium:Tts.
  • A required output may name how long: "path words 430-470". The gate counts the spoken words of the file — the words tts_render would speak, by the parser both share — and fails the step outside the range, naming the count. Every podcast script of the day had overshot its target and every episode ran long with nothing in the gate able to see it.
  • plan_serial_steps. A team-level flag: create_plan makes every step depend on the one before it, whatever the planner declared, and with enforce_node_required_output a step that declares no outputs is filled from its node's declaration at plan time. Applied before the plan is stored, on both the tool path and the hierarchical planner's native call.
  • step_timeout_seconds is enforced. The circuit-breaker value was declared, merged and never read; a run spent 100 minutes on one plan step against a 900-second limit. It is now the wall-clock budget for one plan step: on expiry the step fails with the reason and the run continues to whatever is still executable, the same shape as the other per-step limits. The default moved from 120, which described a per-call limit nothing applied, to 1800; a thinking-model override of 0 inherits the team's value instead of replacing it. Every template that declared a value under the old reading is now held to that number per step.
  • enforce_node_required_output covers the empty declaration. The opt-in held a step to its node's files only when the planner declared a strict subset; a planner that declared nothing switched the gate off. It now holds the step to the node's list in that case too.
  • Init commands report their exit code. The container-provision endpoint returns { command, ok, exitCode, output } per command; the run log reads completed (exit 0) or [INIT-FAIL] with the command and its output.

Changed

  • fullstack-web and api-service no longer hide their install failures. Their init commands were wrapped to discard output and exit status; a failed install produced a container without its toolchain and a log line saying it had completed.
  • podcast-pipeline step timeout 900 → 1800, matching the new default, so the nightly measurement of that template reflects the Engine rather than a number that was never enforced.

Fixed

  • A disabled template verification block keeps its judge_timeout_seconds when the global switch rebuilds the config from it.
  • The tool loop's required outputs are merged by path. A step's list and its node's declaration naming the same file, one with a gate clause and one without, had counted as two obligations; the guard reported "5 of 10" for seven files and held the worker rewriting files that existed until the step timeout.
  • The word gate and the cast gate report together, so one repair fixes both, and an inconclusive verdict logs the judge's raw text beside the reason.
  • The plan contract states the checked deliverables in the step description. The planner's prose kept naming its own paths after the contract had replaced them, and workers, repair bounces and the judge all read that prose; one copywriter "repaired" a file at the prose path while the declared files stayed wrong. One sentence at the end of the description now names the checked paths and supersedes any other.
  • The judge sees the whole step. Files a step wrote that the evidence cap left out are named to the judge as present, and a file the container cannot read any more is read from the host workspace instead; the container is resolved on every verification attempt. One step had been bounced for two "missing" files that existed, then scored 1 on seven read-error strings after its repair round ran in a new container.
  • A speaker tag in parentheses on its own line opens a block. **(NARRATOR - deep voice)** with the words below it had counted as a direction, and the script as zero spoken words.
  • A planner's steps JSON is normalized before parsing. One extra pair of brackets around the array, or quotes escaped one level too many after a retry, are read as the plain array the model meant.
  • The plan contract replaces a planner's own path with the node's. Under enforce_node_required_output the gate already held a step to its node's declaration, but the contract only filled an empty list, so a planner that named its own path had it stated to the worker as the checked one and the worker then failed for obeying. The contract now applies the gate's rule at plan time.
  • A rejected create_plan call tells the retry what was wrong. The parse error and its path go into the next attempt's prompt, and the attempt's log row keeps the arguments; three identical failures in a row used to leave no copy of what the model sent.
  • The script parser reads more of what writers produce. A speaker tag alone on its line (**PIXEL:** (Loud)) opens a block whose words are on the lines below; tags may be title-case (Host, Narrator); a speaker's bare paragraphs continue the block; front matter such as Tone: or Spoken words: is never spoken. Both the word gate and tts_render read the same way. Two scripts of 560 and 900 words had counted as zero.
  • A failed init command fails the run. A non-zero exit is now sticky for the session: every container acquisition after it throws the same message, the plan-step loops stop scheduling, and the run finalizes Failed naming the command and its output. Before, the failure was logged as completed and the agents worked in a container missing what the template installed.
  • SetupOnly had no network during setup. The per-project network is created without masquerade on purpose, and when it was attached the container's default route moved to it, so the bridge never provided egress even before isolation — every network-dependent init command failed. The project network is now attached with a lower gateway priority; the bridge keeps the default route until it is disconnected after the init commands.
  • The trajectory judge asked for its model on the wrong instance. It was pinned to the primary instance, which on a llama-server slot has no preset for the judge model, so every run's automatic evaluation failed at the lease. It now resolves the instance whose loaded model is the judge id, as the step verifier does.

2026-09-15

  • A required output can name a directory, a glob, or how many. An entry in a node's required_output or a plan step's required_outputs that ends in /, /* or /** is satisfied by any file beneath it, and an entry may carry a " >= N" suffix — "menu/images/ >= 10" — which is satisfied only by N files of distinct content. Counting is by content hash, so ten copies of one file count as one. Before this, an obligation could only ask whether a single named file existed, which is the wrong question for work produced at volume: a directory owed twelve photographs passed with one. The suffix is syntax the gate reads and never text an agent sees — every prompt that names an obligation spells it as at least N distinct files.
  • enforce_node_required_output — hold a plan step to what its node owes. A team-level flag, false by default. When a planner declares a strict subset of the paths its node declares, the node's list replaces the step's instead of the difference being logged and dropped. It only ever asserts paths the node already declares, so it cannot hold a step to a sibling step's file. restaurant-kitchen is the one shipped template that opts in.
  • A reporter tool profile. read_file, write_file, list_files, search_files — for a role that produces a report and repairs nothing. A node whose effective profiles include reporter is also exempt from the resolver rule that hands edit_file to content, code and test roles, which previously granted it to every auditor in the role library by name.
  • Dynamic-parallel is selectable in the Designer. The topology pills offered four shapes; the Engine dispatches five. dynamic-parallel now appears beside them.
  • Asset references are checked at the step, not only at the end of the run. A document citing an image or stylesheet no step wrote bounces the agent that wrote it while the loop can still repair it. Workspace-absolute src attributes, which resolve at the server root in a browser and render nothing, are repaired in place; a reference to a file that does not exist is never repaired, because which file was meant is not knowable.
  • Three report-only passes over what a run produced. A step whose reply describes sections its file does not contain, or whose headings have nothing under them; a review that renders a verdict on a file the step never opened; and a set of generated images whose prompts are repeatedly about the same subject. Each names what it saw and demotes nothing.

Fixed

  • A dropped connection to the verifier is no longer a verdict. A transport failure — the stream ending, a socket error — is retried once and logged as such, instead of being classified as an inconclusive verdict and failing a step whose work was already written. A timeout is unchanged and still answered with the health probe.
  • Image generation survives the service's warm-up window. Checkpoint discovery answered "connection refused" or "no checkpoint configured" for the seconds between the image service's HTTP port opening and its model index being built — indistinguishable, to the agent, from a permanent failure of the image it asked for. Those calls are now retried through the window.
  • The count gate reached one topology of three, and its syntax reached agent prompts. A " >= N" obligation on a roundtable or sequential node was satisfied by a single file, and three prompt sites interpolated the raw entry, telling an agent to write a file named menu/images/ >= 10.

2026-09-14

Added

  • The deliverable a run produced, in the client. A run now has two views: the trajectory (what it did) and the deliverable (what it made). The deliverable panel lists every file in the workspace and renders the selection — HTML and SVG in a sandboxed frame, everything else as text — opening on the entry point the Engine detects. Three endpoints back it: GET /api/projects/{id}/deliverables for the tree, /deliverables/raw for one file, and /preview/{**path}, which serves the whole workspace under one prefix so a page's relative assets resolve. Both file routes refuse paths that escape the workspace and send Content-Security-Policy: sandbox, because agent-written HTML must never execute on the Engine's origin. The workspace outlives the container whose ports died with it, so this is the durable view of a finished project.
  • A rendered check over the finished product. Beyond reading the source, the Engine now loads each entry page in a headless browser and reports element lookups that resolve to nothing at run time — the failure where markup and the script driving it are written in parallel and each invents its own names. Scoped to the page's own scripts, and report-only for now.
  • The Engine checks that what it ships parses, and reads its own tools' verdicts. A shipped page that links a stylesheet nobody wrote fails the run; code that does not parse fails the run; and a quality tool that reported FAIL is no longer ignored by the run's own status.
  • Citations to facts nobody wrote are visible. A brief that cites [F1] or [F3.4] is checked against the fact base the run actually produced, in both the dotted and the flat spelling, and against facts declared as an ordered list under a ## Facts heading.
  • Oversized tool output is offloaded, not truncated. A tool result too large for the context budget is written to a file in the workspace and replaced with a notice naming it, so the agent can read the part it needs instead of losing the tail. Content-addressed, so re-reading the same output does not duplicate it.
  • Repair rounds after a deliverable-gate failure. AI_StepRepairRounds (default 2, clamped 0–4) grants extra rounds only when a step failed a typed-absence or required-output gate, on top of AI_StepRounds. The gate message already names the missing path; the previous behaviour was to stop without acting on it.
  • Per-role sampling temperature. A role can set its own temperature, and the value used is recorded with the turn.
  • Four new demo templates. game-night-podcast (sequential, 4 agents), local-threads-retail (hierarchical, 5), restaurant-kitchen (hierarchical, 5, generating food photography through ComfyUI under the run's gpu_services lease) and space-camp-kids (hierarchical, 4). Twenty-six templates ship.
  • Roundtable and sequential teams declare what their nodes owe, and create_plan tells the planner that required_outputs exists, so a plan step can name the files it owes as data.
  • The dashboard sees containers as they are created. The Engine raises ContainerCreated on the container hub, and the Containers view consumes it — a container an agent run created used to stay invisible until something else happened to it.

Changed

  • A project's tier now decides its container's resource limits. Aetherium:Tiers:*:Resources has been configurable since it was written but nothing read it: every team container was built at a hardcoded 2.0 CPU / 4g. A Production project's container now gets 4.0 CPU / 8g and a Temp project's 1.0 CPU / 2g. A team's own default_resources still wins, but only when it differs from the defaults — a block restating 2.0/4g asks for what it would already get.
  • ResourceLimits fields are snake_case. default_resources bodies are now cpu_limit, memory_limit, disk_quota and cpu_shares. Every shipped template had always written that spelling; the camelCase property names meant the block bound to nothing and silently fell back to the defaults.
  • fullstack-web splits its agents across both inference slots. A harness change: results from before it are not comparable with results after.
  • restaurant-kitchen runs entirely on the secondary slot — the team did not fit beside the image model otherwise.

Fixed

  • The roundtable and sequential required-output gates enforce something again. Both passed the gate a synthetic step description naming no path, so the narrowing emptied and the gate checked nothing — on every run of both topologies. They also now record which files each step wrote, so one stage's output can no longer satisfy a later stage's obligation.
  • The SPA's entry point is in the repository. index.html — the file the client build starts from — had never been committed, caught by a broad *.html ignore rule. A fresh clone could not build the client at all.
  • A preview served a product whose own links left the preview. Root-absolute asset paths (/workspace/...) resolved on disk and 404ed over HTTP; the asset auditor now reports them, and the preview resolves them.
  • run_tests never detected a .NET workspace — it reported no framework where dotnet test was the obvious runner.
  • Sixteen tools documented arguments they do not have. Tool descriptions offered parameters the implementations ignore; the catalog is now checked against the methods.
  • A stuck inference slot no longer stops the other from dispatching, and a busy alternate instance holds its tasks instead of failing them (AI_InstanceAvailabilityCheck, default on).
  • An unreachable judge fails the run instead of passing it, and a judge that answered is no longer classified as an outage.
  • Quality checks abstain instead of reporting a defect they could not see. A check that could not run says so rather than reporting zero findings as a pass, and the cross-reference check no longer abstains on the very shape it was built for.
  • An unreachable app is reported as a connection failure, not as a missing element, and the HTML check proves it read a page rather than assuming it did.
  • A file an earlier step already wrote is not owed again, an unresolvable agent id in a plan is logged rather than silently routed to the first worker, and a deliverable that stopped being owed says so.
  • The post-run audit layer reaches document workspaces — roughly four in five projects on a typical host — and any check that can fail a run now has to say why.
  • Reading an offloaded tool result is not offloaded again, which had produced byte-identical duplicate files in the workspace.

2026-09-12

Added

  • A typed reason a step produced nothing. build_steps.Absence records why a step yielded nothing a consumer reads — no tool called, tools but no write, a write that landed outside the paths the build reads, or files that fail the content rule — instead of leaving that difference to 500 characters of free text. The repair instruction an agent is given is now a function of that value rather than of whichever check happened to run first.
  • required_outputs on a plan step. A planner can declare the workspace-relative paths a step owes, as data. When present the required-output gate enforces exactly that list; when absent it falls back to the previous behaviour of narrowing the node's declared outputs by the words of the step's description, which stops guarding whenever a description names no file.
  • A cross-reference check over the finished workspace. At the end of every run the Engine reports scripts whose element lookups match nothing in any page of the workspace — the silent failure where markup and the script driving it are written in parallel and each invents its own names, so the page renders, looks finished and does nothing. A dead reference now demotes a run that would otherwise have Completed. The check abstains, rather than reporting, whenever the page builds its DOM at run time or computes its ids.
  • Outbound search pacing. Aetherium:Research:SearchMinIntervalMs (default 1500, clamped 0–30000) sets the minimum spacing between web_search calls process-wide, so a parallel wave of researchers queues instead of bursting every engine at once. The engines the metasearch backend reports as unresponsive are read back and logged per search, making a degraded search set visible rather than silently thinner results.
  • Per-agent view of a live run. The live trajectory can be filtered to one agent, the agent is carried in the URL so the view is linkable, each agent card on the Monitor screen opens that agent's own feed, and a Live tab appears in the project navigation.
  • More of the Engine's operational surface in the client. Operations gains bridge and port status, container snapshots and live container logs; research runs get their own page.

Changed

  • Tools granted only to a hierarchical supervisor now sit on a worker. A hierarchical supervisor gets exactly one scheduled turn with a single-tool surface, so a tool granted to it and to no worker can never fire — it fails nothing and simply never happens. record_style moved to the finishing team's operational-readiness worker, and analyze_code to a worker on the maintenance team. A conformance test now refuses any template that reintroduces the shape.
  • Research query guidance. Three places told agents to open with one-to-three-word searches; a bare noun returns its dictionary entry rather than a document. The guidance now asks for the technology, then the aspect, then the year where recency matters, and for a query that missed to be re-aimed rather than broadened.
  • Metasearch engine set. Engines are split by how they behave under agent-rate search, so one engine suspending itself no longer leaves the set effectively empty.

Fixed

  • Three tools that take a URL were outside the per-team domain allowlist. The egress gate allows anything it does not recognise as an outbound tool, and http_health_check, validate_web_app and capture_api_schema were all missing from the list it recognises, so a team's allowlist did not apply to them — silently, with no denied call to notice. All three are now gated. This costs the normal quality flow nothing, because loopback and private-range addresses are permitted before the allowlist is consulted; what changes is that an arbitrary public host is refused rather than reached. No shipped template configures an allowlist, so this closed a latent hole rather than one in use.
  • A plan step created through the create_plan tool lost almost everything it was given. The tool documents snake_case keys and its deserializer matched only on case, not on separators, so assigned_agent_id, depends_on, required_tools and required_outputs all bound to null with no error: the plan came back with unassigned steps, no dependencies and no gates. Teams on the hierarchical plan path were unaffected, which is why it went unseen — that path reads the same keys itself. A replay of a recorded run shows five steps the planner had assigned all along, now assigned.
  • The DuckDuckGo fallback behind web_search returned nothing on pages full of results. With no metasearch backend configured the Engine falls back to scraping DuckDuckGo; the scraper required the link's href to precede its class, and DuckDuckGo writes them the other way round, so every result was skipped and the search reported that nothing was found. Results are now read without depending on attribute order, and each result's URL is unwrapped from DuckDuckGo's redirect so it is a source a reader can check and web_fetch can retrieve.
  • run_tests reported that no tests ran over output listing dozens of them — the verdict was parsed after the output had been truncated. It is now parsed first. Python test patterns are passed through correctly.
  • Cancelling a run kept its model residency lease. The next run could then fail while preparing, on a card that was no longer in use. Cancel now releases the lease it took.
  • The evidence ledger did not recognise every tool that reaches the web or the disk, so a step that cited real sources through the knowledge store, or that captured a reference instead of searching, was failed for a defect it did not have.
  • A step that filed reference material was told nothing reads it. The knowledge store sits beside the workspace and is read back later; the step still fails, because reference material is not the deliverable, but the reason it is given is now the true one.
  • The cross-reference check read archived passes from other projects, judging a page against files days old and elsewhere. It now reads only the project's own workspace, skipping dependency and version-control directories.
  • Tool arguments were truncated per row rather than per value, so one long argument took the whole record with it and the URLs a step actually visited were under-reported.

2026-09-11

Added

  • The finishing team. A new finishing-team template (six agents, hierarchical) that runs after a build completes and takes a project from default-styled and functional to customized: Phase 1 researches comparable products, builds three design candidates and writes a brief; the operator answers; Phase 2 applies the answer. It is a design pass, not a code review.
  • capture_reference — a research tool that drives headless Chrome, saves a desktop and a mobile screenshot into the workspace, and returns the page's computed design facts: palette with each colour's share of painted area, type scale, content measure, vertical rhythm, breakpoints and density. Elevated tier; passes the egress allowlist like every other web tool.
  • check_finishing — a quality tool returning one PASS/FAIL row per deterministic mark of a finished project: a favicon every page links, no leftover scaffold markers, a README whose screenshots exist on disk, a version matching the changelog head, and an empty state on every screen that renders a list. It reads only files that ship.
  • record_style — writes finishing/style-record.md and knowledge/style-record.md, taking the approved direction and the rules a later run must not break as prose and generating the token tables itself from the project's stylesheet, so the record cannot document a palette the code does not contain.
  • Finishing gate API — GET /api/finishing/gates (the queue, longest-waiting first), GET /api/finishing/gates/{projectId}, POST /api/finishing/gates to open one by hand, and POST /api/finishing/gates/{projectId}/answer to answer the brief and release Phase 2. The answer is written into the workspace before the gate is stamped.
  • Aetherium:Finishing:AutoTrigger (default false) — when true, a build run reaching Completed opens a finishing gate for that project. Four SystemSettings rows — Finishing_DirectorModel, Finishing_DirectorInstance, Finishing_WorkerModel, Finishing_WorkerInstance — override the models the template pins, without editing it.
  • finishing_gates table — one row per pass: its phase, both run GUIDs, and the timestamps that record when the brief was written, when the operator answered, and when Phase 2 closed.
  • A binary-path guard on write_file and edit_file. Text at a path whose extension names a binary format is refused, and the refusal names the tool that does produce that file.
  • An image manifest. comfyui_generate and capture_reference append each file they save to images-manifest.md at the workspace root, so a later step learns the image exists instead of inventing a path.

Changed

  • check_finishing grades only what ships. Dot-directories, node_modules, dist and build are skipped, so a build's own leftovers can neither fail a project nor pass it.
  • Measured VRAM costs for three more models, including the box's largest occupant, which had no entry and was therefore budgeted by fallback.
  • A step declares which of its node's required outputs it is asking for, rather than owing the whole run's output path.

Fixed

  • A root-run container init step no longer breaks host-side tools. container_init_commands run as root, so any directory they create is root-owned; ownership of the workspace is now restored afterwards. Before this, tools that write into the bind mount from the host — capture_reference, comfyui_generate — failed with permission errors while write_file beside them kept working, because the Docker daemon extracts its tar as root.
  • A stale Running row no longer blocks a new pass. Liveness is bounded by recency.
  • A completed run that wrote no brief no longer advances the gate, and a brief that was written survives a failed sibling step.
  • A model id alone no longer moves an agent to another instance. A node pinned to a model without an instance resolves to the primary slot; the finishing template pins both per node.

2026-09-10

Changed

  • Worker reasoning reaches every shape. A team's config.reasoning_effort now applies to every worker dispatch — single-agent and sequential-stage turns, roundtable rounds, dynamic-parallel subagents and the lead's synthesis — not only to hierarchical delegates. A template that sets it and runs one of those shapes starts a new comparability era.
  • Run overrides reach spawned subagents. modelId, preferredInstance, maxTokens, contextWindow and reasoningEffort in the create/run-from-template overrides also set the team's subagent_defaults, so a dynamic-parallel run's children follow the override instead of the operator default.
  • Teams with ComfyUI tools are no longer moved off the primary instance. The pre-lease re-route that sent every agent of such a team to the secondary instance is retired; the run's gpu_services lease budgets ComfyUI beside the team's models instead.

Fixed

  • comfyui_generate returns paths an agent can use. Each finished image is downloaded into the run's workspace under save_dir (default images/; the router forwards the argument) and the tool returns /workspace/<save_dir>/<file>.png per image instead of an internal type name, with the PNG left only in the backend's output folder. A step that generated images counts as delivered even when it wrote no text file.
  • Workspace paths are normalized before they reach the container. A path argument with surrounding whitespace, or one written with a leading relative workspace/ segment, no longer creates a file one directory deeper than the plan asked for.

Added

  • Evidence gate for reporting roles. A role can be marked requires_evidence (the shipped culture-reporter, podcast-researcher and research-analyst roles are); a plan step run by such a node fails unless the agent called web_search or web_fetch, wrote at least one source URL, and every URL it wrote appeared in a search result or a fetch during the step. Only calls that succeeded count. The failure message names the unseen URLs so the next round can repair.
  • ModelUnloadVerifyGraceSeconds (default 15): after the unload wait, the verify keeps polling for the model to leave before the unload counts as failed, since a backend lets an in-flight request finish first.
  • Two templates ship: code-review-panel and poster-pipeline. code-review-panel is a roundtable of security, correctness and maintainability reviewers over one code brief — two rounds, independent findings then reconciliation, each reviewer filing a verdict with severities and one required change. poster-pipeline is the poster job as a two-stage sequential team under a leased ComfyUI: the image artist generates and records the real image paths, the copywriter writes the sheet from that record. Neither has a planner, so neither can drift off the plan.
  • Three catalogued reviewer roles — security-reviewer, correctness-reviewer and maintainability-reviewer — are offered to Quick Create alongside the existing library.
  • A rejected step's files leave the workspace. When a plan step fails the evidence gate, the files it wrote are moved to .evidence-rejected/step-N/, and any later step whose description names one of them is corrected before it runs — it may not read, list, verify, link, count or replace the withdrawn file. Previously the files stayed writable and a later step could refill them with placeholders and report the issue complete.

Changed (harness)

  • The scheduler retries a dropped model call once. A half-read response, a socket error, a router proxy error or llama.cpp's harmony-parser rejection is retried after a short pause before it reaches the caller; "model is not loaded" keeps its reload path and a timeout is still the caller's budget.
  • A failed standard-set restore is retried. The slot stays marked and the arbiter's sweep retries with backoff, three attempts, instead of giving up after one; the trajectory judge holds a batch lease on its slot while it scores a run, so the restore waits for it.
  • A collected subagent swarm is stopped. When a dynamic-parallel swarm outlives the session budget, unfinished subagents end at their next round and their queued model calls are dropped before the lead synthesises.
  • An evidence gap no longer ends the run. A step that wrote files but failed the evidence gate is recorded as skipped, not failed: the run summary names the unsourced material and sets it aside, the supervisor does not re-plan, and the remaining steps continue with what is sourced. A step that wrote nothing still fails. The instruction the rest of the run reads now forbids substituting, advertising or promising the missing material and requires exactly one plain sentence disclosing its absence.
  • The evidence gate stops offering an escape it has not earned. A step whose files cite no source is told to cite the URLs it already saw; the "write a flagged no-results file" route appears only when no search or fetch returned anything. The three shipped reporting roles were reworded to match, one of which had promised that a flagged file always ships.
  • A truncated swarm reports honestly. A subagent cut short when the swarm is collected is recorded as skipped rather than failed, and the spawn step succeeds when at least one thread landed a file, so a run whose lead still wrote the synthesis is not failed for the threads that did not finish.
  • Plans stop promising artifacts the team cannot make. Before a plan is stored, a step whose description promises a binary file at a workspace path gets one CONSTRAINT: sentence appended when the agent that step is assigned to has no tool that produces that kind of file. The sentence names the markdown path to write instead, rather than leaving the agent to invent one, and asks it to reference each generated asset by the exact path the tool returned. The check asks the assigned agent's tools rather than the team's, and a step that says to assemble, composite, combine, merge, lay out, overlay or compose is constrained regardless, because generating a picture from a prompt is not the same as laying copy over one.

Fixed (dynamic-parallel)

  • spawn_subagents accepts a single search_hints string (coerced to a one-element list) instead of refusing the whole spawn.
  • A dynamic-parallel run whose lead never spawned finalises Failed, saying so, instead of Completed with the lead's last message as its result.

2026-09-09

Added

  • Three more shipped templates. podcast-pipeline (sequential: researcher → scriptwriter → show-notes editor, every script line carrying a fact reference the editor audits), pitch-panel (roundtable: three critics score one pitch through different lenses and revise over two rounds) and poster-studio (hierarchical: art director, copywriter and an image artist generating the poster images with ComfyUI).
  • gpu_services on a team. A template names the registered GPU services its run needs (comfyui, qwen-tts); they ride on the run's own inference lease, so the service starts before the run, its VRAM is budgeted beside the run's models, and it stops after the run.
  • More run overrides. maxTokens, contextWindow and reasoningEffort join the create/run-from-template overrides, so a shipped template runs on a model it was not authored for without a template variant.
  • pop-culture-roast template. A four-agent hierarchical newsroom (editor-in-chief, culture reporter, roast columnist, copy editor) that ships a pop-culture magazine issue as files: sourced features, a roast column under house rules, a front page with a fact-flag list.
  • Per-template worker reasoning. A team's config.reasoning_effort (low/medium/high) turns a thinking model's reasoning on for that team's worker turns; AI_ReasoningEffort still covers planning turns only. Off unless a template sets it — and a template that sets it starts its own comparability era.
  • run-from-template can return early. Wait: false creates the project, starts the run in the background and answers 202 with the project id and Status: "started", so a caller with a short request timeout gets the id instead of a timeout.
  • GPU services under leases. A lease may name a registered GPU service (Aetherium:VramOrchestration:Services: a systemd user unit, its slot and its VRAM claim). The arbiter starts the unit under the lease, budgets its claim like a resident model — evicting what it cannot fit beside — waits for its health URL, and stops it when no live lease names it. A lease may name services and no model. The service units ship with the publish output and scripts/deploy.sh installs them.
  • The Inference page. A new page in the web client shows every GPU slot's residents, leases (with a release button), queue, services, foreign occupants and schedules.
  • Registered inference schedules. A timer or campaign can declare what it will need and when (PUT /api/inference/schedules/{consumer}: slot, models, cadence, expected duration); the queue view lists each slot's schedules so "what is the GPU doing tonight" has an answer before any lease exists. Rows persist in the Inference_Schedules setting. Registration reserves nothing.
  • A box-level inference arbiter: leases on each GPU slot's VRAM. Every consumer of a slot now holds a lease — the Engine's own runs (run priority), an external chat client through the new OpenAI-compatible /v1/chat/completions relay (interactive), and batch jobs through api/inference/leases (batch). Grant order is run > interactive > batch: a run evicts an interactive lease at once and is never displaced by one; both preempt batch work cooperatively, with Aetherium:VramOrchestration:PreemptGraceSeconds (20 s) to release before the model is unloaded. A batch lease that cannot be granted queues and is granted by the sweep when the card frees up. GET /api/inference/queue shows what each slot holds and who is waiting. An interactive lease idles out after AI_InferenceLeaseIdleMinutes (default 15).
  • llama.cpp llama-server as a per-slot inference backend. Aetherium:LMStudio:Instances:<slot>:Backend is LmStudio (the default) or LlamaServer. On a llama-server slot the Engine lists, loads and unloads models through the router's own API, a model's context length is a property of the server's preset rather than a load-time request, and a model missing from the preset list is rejected before a run starts. The preset file and a user-scope systemd unit ship with the publish output; scripts/deploy.sh installs both and restarts the router only when one of them changed. Runs on a llama-server-backed slot are a new measurement era and are not comparable with earlier results.
  • AI_ReasoningEffort. low, medium or high turns a thinking model's reasoning on for the two planning turns only (plan creation and the Dynamic-parallel lead's planning phase); every other turn keeps reasoning off, as before.
  • Prompt-cache telemetry. When the inference server reports it, each model call records how many prompt tokens were reused from the server's prefix cache versus processed, in the aetherium.prompt.cache stream event and the run log.
  • Trajectory verdicts on the run viewer, and an opt-in auto-eval. A run's page now shows the judge's latest verdict — score, summary, per-dimension scores, issue count, judge model — and a Score trajectory button that requests one. With AI_TrajectoryEvalOnRunComplete set to true the Engine scores every finalized run in the background; it is off by default and never gates a run. Trajectory evaluation had shipped in June with an endpoint and no caller.
  • Shell history per container on the Operations page. Every run_shell an agent executes has always been recorded with its stdout and stderr; nothing read it back. Each container card now carries a collapsed history: exit code, agent, duration, the command and what it printed.

Changed

  • web_fetch returns more of the page, and its links. The text cap is 20,000 characters (was 10,000) and the reply ends with up to 40 same-site article links, so an agent can open a story from a front page instead of quoting its teaser.
  • web_search no longer pins search engines. The query goes to SearXNG's enabled engine set; a suspended engine no longer empties every result.
  • An interactive lease is refused while the slot is busy. Any live run or batch lease on a slot — even one holding the same resident model — now fails an interactive request with a "busy" reason, so the /v1 relay answers the router's 400 and a chat client with a failover chain moves to its next hop until the job releases.
  • The /v1 relay serves every inference slot. An LM Studio slot resolves by its catalog and forwards to its own endpoint under the same resident-only interactive lease; POST /v1/responses is relayed beside /v1/chat/completions; GET /v1/models lists every slot's catalog with a load state. A slot listed in Aetherium:VramOrchestration:ProtectedStandardSetSlots keeps its standard set through every lease (a batch lease there must fit beside it). An LM Studio that shares a llama-server slot's card (CoResidentLmStudio) is emptied before the arbiter loads there and shown as a foreign occupant. A batch lease that would still hold a card when a registered schedule needs it with other models is refused.
  • An interactive inference lease is resident-only. The /v1 relay's lease is granted only when the model is already on the card; it never loads, unloads or preempts anything. While a run or a batch job holds the slot the relay answers the router's 400 and the chat client's fallback chain advances, so an interactive client never shares or reshapes a card another job is using. A run still preempts batch work cooperatively and evicts an interactive lease at once.
  • The standard set is a restore target, not a protected set. A run (or a batch lease) whose models do not fit beside an instance's StandardModels now evicts them and the arbiter restores them once the slot is idle, where before the run was refused with the arithmetic. Models held by another live lease, the embedding model, and models the Engine did not load are still never evicted; a refusal now names the lease that holds the card. On a llama-server slot every resident model counts as Engine-owned, so a model left resident across an Engine restart is no longer immovable. Residency only — model behaviour and results are unaffected.
  • fullstack-web scaffolds the rooted application its prompts describe. The template created frontend/ and backend/ trees while every role prompt resolved into it forbade inventing a frontend/ or backend/ directory, and its project-config.md named two ports. It now scaffolds src/, public/ and tests/, and the config describes one rooted Node application on 3100 serving pages and API, with 3101 reserved for a separate frontend server only when the task asks for one. Results from this template either side of this are not comparable.
  • Auto-snapshot before an idle stop is back on, for Production and Staging projects only. Container_AutoSnapshotBeforeCleanup seeds true again; Temp-tier projects are never snapshotted.
  • Lessons and session summaries outlive their project. Both tables were always empty because every row went with the project that produced it. project_id is now nullable with ON DELETE SET NULL. Lookups are still by project, so a retained lesson is not injected into any later project's prompts.
  • Five team-config fields nothing read are gone: keep_alive_minutes, default_isolation_mode, shared_workspace, container_settings, load_balancing. They are removed from the shipped templates; a template that still carries them is accepted and the fields are ignored. The idle timeout is the Container_IdleTimeoutMinutes setting.
  • Nine controllers with no caller are removed: api/agentdeployments, api/teams, api/agent-association, api/memory, api/projectfile, api/scheduler, api/benchmark, api/test, and the two absolute routes api/projects/{id}/tools/execute and api/projects/{id}/chat. The AgentDeploymentUpdated hub event and the always-zero Deployments dashboard tile go with the first. The agent_deployments and agent_files tables and the executed_by column of container_command_logs are dropped.
  • The seven PowerShell lifecycle scripts are removed (start-aetherium.ps1 and siblings); scripts/deploy.sh and start-aetherium-linux.sh remain.
  • The step verifier is scheduled as live work. Its judge call ran at background priority and was read only when the normal queue was empty; with parallel plan steps feeding the primary instance it could sit unserved for its whole budget and time out, and an inconclusive verdict fails the step. The judge is now queued at agent priority, and when the team does not name a verifier_preferred_instance it is sent to the instance whose loaded model id is exactly the configured verifier model rather than always to Primary. Verifier-gated results either side of this are not comparable.
  • A subagent that still owes its output file is told to write it before a repeated-tool streak ends its turn. The streaking tool is withdrawn and the agent is asked to write the assigned file from what it has; a second streak ends the turn as before.
  • fullstack-web frontend and QA prompts name where pages live. The frontend role reads project-config.md first, puts pages in public/ by default rather than "for example", and treats a 404 from / in its mandatory curl step as something to fix. The QA role must run a validate_web_app html check on / expecting the page title plus an http check per API route. Results on templates built from fullstack-web either side of this are not comparable.

Fixed

  • comfyui_generate on a fresh install. The plain-prompt SDXL graph loads the checkpoint from the ComfyUI_Checkpoint setting, else the first checkpoint the install reports, instead of a hard-coded file name that only resolves where that one checkpoint is present.
  • Auto-snapshot no longer fails on a deleted project or leaves orphan images. The cleanup sweep snapshotted containers whose project row was already gone; the insert failed on the foreign key after the docker commit had run, leaving a ~1 GB untracked image each time. The snapshot is now skipped when the project no longer exists, and an image whose row cannot be written is removed again.
  • A run pinned to an instance that also holds the embedding model no longer fails before its first step. The pre-run residency check reported the embedding model as unexpected and aborted; models the plan deliberately leaves resident are now reported, not fatal.
  • Trajectory verdicts are consistent and name their judge. The headline score is the rounded mean of the dimension scores, needs improvement follows from any low dimension or a high/medium issue, and the verdict records the model that actually answered. The judge's token budget is raised so a long run's verdict is no longer cut off mid-JSON and scored 0.
  • validate_web_app now honours expect_status, expect_fields and expect_contains. The fields the tool description asks for had never bound to the check object, so an html check with expected content always passed and an http check only failed on a dead port or a non-2xx/3xx status. A check that is not valid JSON now says what shape was expected.
  • The step verifier's 90 s budget starts when its call is dispatched. It used to count from enqueue, so a model reload or a queue wait on the judge's instance could time out a judge that was still answering; dispatch has its own 5-minute allowance.
  • A truncated reply is visible outside the tool loop. The plain chat path never read finish_reason, so a reply cut off at max_tokens looked complete to everything but the tool loop; it is now recorded on every path.
  • The embedding health check probes both instances. It only ever probed Primary, so an embedding model resident on Secondary was reported missing.

2026-09-08

Changed

  • The supervisor model in five shipped templates could not load, and now can. api-service, steam-frame-vr, maintenance-team, business-analysis and documentation-team pin a 14B reasoning model as their supervisor. At 8-bit it exceeded the VRAM budget on its own, and with no measured cost the planner priced it above the room left beside the resident worker — so those runs never had the supervisor their template describes. It now ships as the 4-bit quant with a measured cost entry and its KV cache forced to system RAM, and loads beside the worker with room to spare. A quantisation change is a model change: results from those templates either side of this are not comparable.
  • Shipped templates no longer pin a model that cannot load. quick-research, api-service, quick-script, quick-script-js, quick-fix, steam-frame-vr and fullstack-web, and the code slot of the team-composition model policy, pinned a 24B model. The Engine plans every load against a VRAM budget and refuses a pin it cannot satisfy, and that model does not fit beside the protected standard model on a 16 GB card — so every node on that slot failed to load. All of them now pin google/gemma-4-e4b, and the team generator no longer suggests the 24B model when it invents a coder. This is a hardware verdict, not a quality one.
  • Reasoning models now actually get the reasoning ceilings, and thinking_model_pattern is gone. A team's thinking_model_overrides were opted into by a regex on the model ID kept beside them on the circuit-breaker config. It matched none of the models this engine runs, so every run used the ordinary limits while a second, measurement-backed capability table said the model reasons. That table is now the only classifier, and the removed thinking_model_pattern property no longer appears in a team's circuit_breaker. For a team whose default is a reasoning model the effective limits change in both directions: turns per agent 25 → 35, turns per step 5 → 12, tool calls per step 25 → 60, step timeout 120 → 600 s, but tool calls per session 200 → 140 and session timeout 3600 → 1800 s. Benchmark numbers either side of this are not comparable.

Added

  • Roundtable and Sequential ship as templates. roundtable-review (three documentation peers reviewing the same brief over two rounds, no supervisor and no delegation) and sequential-docs (the same three roles as a three-stage pipeline, each stage handed the previous stage's output). Both topologies had existed in the orchestrator with no shipped example; every topology now has one to start from.

Fixed

  • Dynamic-parallel teams actually delegate. A research lead's subagents were never given the output path the lead assigned them, so each wrote where it liked and the engine reported all of them as having written nothing — the lead was then told not to read files that were on disk, and synthesised from nothing. Three defects behind it: the assigned output_file did not bind (snake_case keys are not matched by case-insensitive binding), an omitted-as-null search_hints then crashed each subagent before its first model call, and a crashed subagent was dropped silently while the lead was told it had completed. Subagents now write the paths they are given, a subagent that fails is reported as failed, and the lead's synthesis is built on the files that exist. Results from dynamic-parallel runs before this change reflect a lead working alone.
  • A webhook target that cannot authenticate is skipped, not retried forever. A target whose secret is still the REPLACE_ME_VIA_LOCAL_OVERRIDE placeholder is skipped with one warning; the shipped appsettings.json target is disabled for that reason. Set a real secret in appsettings.Local.json and enable it there.

2026-09-07

Changed

  • The planning turn offers create_plan alone. A supervisor that answers the plan request with read_plan no longer has that option: the planning turn's only tool is create_plan, and a supervisor that has not produced a plan after three attempts fails the run instead of being handed to unplanned delegation. Every Hierarchical team, whatever its size, takes the plan path; the unplanned delegation loop is gone.
  • An inconclusive verdict fails the step. When the step verifier times out, answers in prose, or cannot run, the step now fails with the reason instead of passing as "inconclusive", and build_steps.VerificationStatus records passed, failed, inconclusive, or null for a step that was not verified.
  • A declared preferred_instance is a pin. The scheduler no longer moves a pinned call to the other LM Studio instance, loads its model there, or runs it on whatever model that instance holds; the call fails with the reason. Two fallbacks are gone for every call: running a model that is downloaded nowhere on whatever is loaded, and the "last resort" that answered a request for one model with another. Unpinned calls may still move to an instance serving the same model.
  • A failed LM Studio probe is unknown, not empty. Anything but a 200 from /api/v0/models is treated as no answer: no /v1/models name-only fallback, no load into an instance whose probe failed, no pre-warm into an instance that has not answered, and an unhealthy probe keeps the last known model instead of recording an empty instance.
  • Knowledge writes need a real embedding. write_knowledge returns ok:false with the reason when the document could not be indexed (for example, no embedding model is loaded); the file is still saved. search_knowledge reports a search that could not run as unavailable instead of answering "no results". Placeholder hash embeddings are gone.
  • Fail-closed defaults. The write_file shrink and reference guards refuse the write when the check itself errors; the cleanup sweeps skip a project whose team config cannot be read instead of treating it as unprotected; a failed tool-schema discovery raises instead of offering agents no tools; a port probe that times out counts as unavailable; a run whose build record cannot be written fails; and a container file operation that fails is an error, never a silent write to the host filesystem.
  • An instance missing from the LM Studio registry refuses to load. The legacy path that shelled out to the lms CLI on the Engine's own host is removed.
  • The Engine no longer chooses models on its own. The idle-model list ships empty, so no model is loaded at start-up or reloaded after a run, and an instance with no idle model configured is left holding whatever it has. The default context length is 49152. The knowledge-seeding gap-detection call now requires its configured model to be loaded already and fails with the reason rather than triggering a load.
  • Snapshotting a container before cleanup is off by default. Container_AutoSnapshotBeforeCleanup now seeds false; turn it on per deployment if you want the pre-destroy snapshot.
  • One hard-coded prompt guard is gone. A guard that watched for a specific pair of filenames and ordered the agent to write the second one has been dropped from the default tool-loop pipeline; the deliverable gate already covers a step that produces nothing.

  • VRAM orchestration plans against the card instead of clearing it. Preparing a run used to unload everything on each instance the template named and then load into the empty card, which evicted the embedding model and whatever else was loaded, every run. It now probes each instance, plans against what is already there, and loads only what is missing. It never unloads the embedding model or a model the Engine did not load itself, and when a run's models cannot be made to fit it fails with the arithmetic — what was needed, the budget, and what is being held — rather than evicting something to make room. Costs come from measurement rather than file size, because a model's real footprint can be either side of it, and because some models keep their key/value cache in system RAM where context costs no VRAM at all. Every load in the Engine now takes this path, including the scheduler's mid-run swaps, the team generator's first load and the direct chat endpoint. After a run each instance is restored to its configured standard set. This changes what a run sees; results either side of it are not directly comparable.

  • Affinity pins come from the template. A run's model-to-instance pins are taken from the preferred_instance its nodes declare, rather than from a supervisor/worker heuristic that scored swap counts and could pin a layout the template contradicted. An instance is pinned only when exactly one model targets it and its standard set does not name a different one, so a pin cannot collide with the verifier's own instance-pinned calls.
  • Per-run VRAM orchestration is on by default on Linux, having been switched off since the behaviour above made it unsafe to run.

Removed

  • Per-agent container isolation and the container pool. Both paths were unreachable — the warm-up routine was never called and the isolation flag shipped off — and both are now deleted, along with the Feature_AgentIsolation, Container_DefaultPoolSize and Container_PoolIdleTimeoutMinutes settings. Every agent on a project shares that project's container, which is what the running system always did. A template's default_isolation_mode is still accepted and stored, and is now read by nothing.
  • The OpenClaw model switch. VRAM orchestration used to switch the companion assistant to a cloud model around each run by shelling out through WSL — to a host that no longer exists, using a command whose effect live sessions do not pick up without a restart the Engine cannot perform. The contention it addressed is handled by the protected standard set instead. Its five settings are gone with it.
  • The router's instance-selection half. GET /api/lmstudio/best-instance, /api/lmstudio/profiles and /api/lmstudio/recommendations are gone, with the agent-to-instance mappings, the load-balancing flag, the latency-scored instance choice and the model performance profiles behind them. Choosing an instance is the scheduler's job, and the router is now only the store for the two-slot instance configuration and its in-flight counters. GET/POST /api/lmstudio/config are unchanged.

Fixed

  • A port-allocation failure fails container creation. A team container is no longer created without the ports it declared, which left its app unreachable on the host with no recorded cause.

2026-09-06

Added

  • A conformance suite for the model harness. Tier 2 tests replay clean trajectories through the Engine's tool guards and keep a registry of which guard fired where, so a guard that starts bouncing a healthy run is caught before a benchmark is.

Changed

  • One system message, at index 0. Memory context, lessons and per-turn injections each added their own system message, and strict chat templates reject a second one with HTTP 400 — four of thirteen models measured on 2026-09-06 failed every project that carried memory context. The scheduler now folds every leading system message into one and re-rolls a late one as a prefixed user note. Prompt assembly differs from every earlier run.
  • The step verifier's judge call is streamed. The non-streaming call dropped or timed out on about one in five verdicts and the step passed as "inconclusive". Streaming assembles the same reply from the event stream. Verifier-gated results before and after are not comparable.
  • Model capability rules follow the 2026-09-06 sweep. qwen3 GGUFs ignore enable_thinking and get the prompt-level /no_think switch; qwen3.5 is declared unsuppressible; gemma-4, nemotron-3, bonsai and gpt-oss are declared thinking models with no switch.
  • Role prompts no longer dictate layout or ports. The composition roles for API and fullstack teams used to hardcode npm init -y, a backend/–frontend/ split and ports 3101/3100 regardless of the task; the task and project-config.md now decide, workers start the app with npm start, and the SQLite package ban is lifted. The API service template runs on node:20 (full image) so native modules build.
  • Agent skills retired. The load_skill tool, its loader and the Aetherium:SkillsDirectory key are gone; the feature never shipped in a published build. The tool count is 32 across the same 10 server classes. The manifests are archived in the repository.
  • Removed dead configuration. The Aetherium:Docker and Aetherium:Pools sections were bound but never read; both are gone. The TempProjectRetentionDays setting row (superseded by the per-tier retention keys) is deleted by migration.

Fixed

  • Step verdicts were cut off by the judge's token budget. The verifier allowed the judge 1,024 tokens for its verdict; a reasoning model spends most of that thinking before it writes the JSON, so the verdict arrived truncated and the step passed as "inconclusive". The budget is now 4,096 tokens. Verifier-gated benchmark results before and after are not comparable.
  • Port-allocation history survives a project delete. The allocation's project reference is now nullable with ON DELETE SET NULL, so deleting a project no longer takes its port rows with it; the reaper releases allocations whose project is gone.
  • The container-pool settings are read. Container_DefaultPoolSize and Container_PoolIdleTimeoutMinutes were shown in Settings but the pool used constants; it now reads them per pool, falling back to five and thirty minutes.
  • A migration that would not have applied. The migrator refused to run because the model snapshot and the model disagreed on the new port-allocation relationship; the relationship is declared, and a test now diffs the snapshot against the model so a hand-written migration cannot drift silently again.
  • Retired services and DTOs. Four DI-registered services nothing resolved, the LoadBalancingService, the AgentContainerAssignment model and two OpenClaw task DTOs were removed. No endpoint or behavior changes.

2026-09-05

Added

  • Provision a project's container without a model run. POST /api/container/project/{projectId}/provision creates the container through the same path the orchestrator uses, so the port allocator, the container user and the template's init commands are the real ones. It reports the live host-port binding rather than the requested one, and each init command's result rather than swallowing failures. It refuses a team whose network policy is not Unrestricted, since provisioning it this way would drop the isolation the team asked for.
  • A write-probe for container file ownership. POST /api/container/project/{projectId}/write-probe writes a file through the file tool, then reports through the shell whether the shell user can modify it. The two paths disagreeing makes npm install fail with EACCES on files the agent just wrote, and it is invisible to any test that mocks the container.
  • Host ports are allocated per workspace tier. Production takes 3000–3099, Staging 3100–3199, Temp 3200–3499, each overridable in configuration. Production is small and allocated bottom-up so a kept project tends to keep its port across restarts, while Temp is large because that is where the churn is. GET /api/openclaw/ports/pool reports each band's health and POST /api/openclaw/ports/reconcile releases allocations whose container is gone.

Fixed

  • project-config.md gave agents the host path instead of /workspace. The file is written twice — once at the project root for operators, once inside the directory the container mounts — and both copies resolved the workspace-path placeholder to the host path. Every role prompt opens by reading this file, so on every template and every run the agent's first read named a directory that does not exist in its own filesystem. The two copies now resolve it separately.
  • The repeated-tool guard mistook scaffolding for a loop. The rule was seven consecutive calls to one tool, which cannot tell a model writing seven distinct files from one rewriting the same file. A streak now trips only when the work repeats — seven consecutive calls touching at most two distinct targets — with an absolute limit of 20 to bound a runaway writer. Tools that name no path or command keep the previous threshold exactly.

2026-09-04

Added

  • Quick Create is back in the web client. The Projects page's create form takes an optional brief; with one, the team generator designs the team — recipe, topology, library roles with their tools and models — the project is created with it, and the Designer opens for review. The Designer header gains a generate control that adds one agent from a one-line description. Projects created from the form now get a workspace folder named after the project instead of a numbered tier folder.

Changed

  • Backend roles steer SQLite to sqlite3 or sql.js. The fullstack and API backend roles say not to use better-sqlite3, whose native build fails in the slim Node image. The quick-script (JavaScript) template now publishes port 3000 so its app can be reached.
  • User Guide rewritten for the web client. Every page and screenshot now describes the current interface: the grouped sidebar, the project bar (Studio, Designer, Monitor, Runs, Versions, Settings), steering and stopping a run, trajectories, approvals, and the settings table.
  • api-service and fullstack-web install curl in their container init commands; node:20-slim ships without it and the code roles use it for health checks.
  • edit_file refuses a multi-match replace that would outweigh the text. When the search matches more than once and the replacement's length times the match count exceeds the length of the text being edited (the range, or the file), the edit is refused with the counts and the whole-file form. A rename that matches every line still replaces every match; a single match may still expand. Every team whose agents hold edit_file runs on a different tool contract from this change.
  • Three presets marked Demo. business-analysis, writers-room and steam-frame-vr carry the category Demo and say so in their descriptions; they are kept for the showcase and are exercised by no benchmark. Files and behaviour are unchanged.

Fixed

  • Files agents write are owned by the container user. write_file and edit_file copied files into the container as root while shell commands ran unprivileged, so a later npm install could not rewrite package.json. Written files now carry the same uid:gid the shell runs as (Linux).
  • run_tests no longer reports PASSED when nothing ran. A missing test script, no framework, no tests found, or a non-zero exit with no summary now reads NO TESTS RAN — <reason>; PASSED requires passing tests and no failures.
  • validate_web_app http checks expect success. A check without expect_status passes on 2xx/3xx only; a 404 on /health was reported as a pass before.
  • The planning turn recovers from read_plan. A supervisor that answers the plan request by reading the (nonexistent) plan is told what that returned and asked again, instead of the run falling back to a loop that never executes the plan.

2026-09-03

Added

  • Role library, recipes and model policy. Three composition files beside the tool profiles: roles.json (every shipped agent role — one prompt, one tool list, a model slot — plus a generic team-lead), recipes.json (the five team shapes with their edge rules and circuit-breaker defaults) and model-policy.json (the benchmarked model per slot with its evidence). A team node may name a role_id and a team may name a recipe; the Engine fills in everything the file leaves out, and refuses to start when a composition file is unsound.
  • Quick Create chooses roles, not tools. The team generator now returns a recipe and roles picked from the library (with project-specific prompt additions), or custom roles described by tool profiles; tools and models come from the roles. The prompt and its JSON schema are built from the composition files, so a new role or recipe reaches the generator without a code change. Generated teams are on a new harness from this change; template-driven runs are not.
  • Session fork. POST /api/agui/{projectId}/fork re-issues one recorded model exchange from a position in a run's event log — a different model or instance, temperature, max_tokens or seed, a line appended to the system prompt, or the last message replaced — up to twenty samples at once, and logs the lineage as aetherium.session.forked. GET …/fork-points lists the positions a run can be forked from. One exchange, not a resumed run, and only for runs that were recorded.
  • Reasoning as its own event stream. Every agent reply and verifier verdict that carried model reasoning now publishes the protocol's REASONING_START … REASONING_END bracket ahead of the message, with a flag when the reasoning was cut off. The run ledger shows a "reasoned" mark with a collapsed disclosure, and warns when the reasoning was truncated.
  • Fresh rounds per plan step. AI_StepRounds (default 1, the previous single attempt) gives a plan step up to that many fresh, history-free attempts; each round receives the same step instruction plus a validated handoff from the previous round — files written, tools used, a bounded summary, verifier findings — and each round is recorded as an aetherium.step.round event with the Engine's own verdict, not the worker's claim.
  • update_plan takes a step number. The tool now identifies a step by step_number (the 1-based number read_plan shows and depends_on uses) or by step_id; a step_id that is not a step id but matches a step number on the active plan is taken as that step, with a note in the response. Previously the only accepted key was the database id, which models routinely confused with the step number.
  • Refused tool calls are recorded. Every tool call the Engine refuses before dispatch is published as an aetherium.tool.requested event with a reason — not-in-node-tools, not-in-allowlist or catalog-miss — so the demand for tools a role or the catalog lacks can be counted per role.
  • Tool profiles and one resolver for a team's tools. A node may declare profiles (reader, writer, coder, qa, researcher, planner, orchestrator, knowledge, imaging, comms) and exclude_tools; a single pure resolver expands them and applies the supervisor wiring, planner grant and edit rule on every creation path — Quick Create, template create, role sync and Studio edits — logging each tool it adds. The profile vocabulary and the keyword tables it uses live in Templates/composition/tool-profiles.json, validated at startup.
  • Create overrides. Template create, run-from-template and quick-create accept overrides (modelId, nodeModels, preferredInstance, temperature, stepRounds, toolRequests), applied to the team before it is persisted and logged. A team's step_rounds overrides the AI_StepRounds setting.
  • Resolved-team preview. POST /api/template/{id}/resolved and POST /api/template/resolve return the team as it will run, plus every tool the resolver added, removed or overrode.
  • The tool-request gate. A team's tool_requests policy (deny by default, grant, or ask) decides what happens when an agent calls a tool its node does not hold: the ask is recorded, then refused, granted for the rest of the run when the tool exists and sits inside the node's profile expansion set, or put to a human through the tool-approval path. Decisions are aetherium.tool.granted / aetherium.tool.denied events, and GET /api/agui/tool-requests reports asks and decisions per agent and tool.
  • search_files tool. Read-only locate step in the workspace group: file names by glob, lines by text or regex (query, path, glob, regex, max_results), output capped, meant to replace run_shell with ls, find or grep. Every shipped role that lists files holds it.
  • todo tool. An agent's own scratch list for the run in the planning group — write, check, read — separate from the team plan, process-local, released when the run ends.
  • edit_file v2. Exact match first, then a whitespace-tolerant match when it lands in one place; an empty search creates a missing file; an optional start_line/end_line range replaces those lines or confines the search; a miss names the nearest line. The tool count is now 33 (25 Standard, 7 Elevated, 1 Restricted).

Changed

  • edit_file whole-file rewrite. A range from line 1 to the last line with an empty search replaces the whole file; an end_line past the end is clamped to the last line and the result says so. Because a line range states explicitly what is replaced, it is not run through the write_file shrink guard. A miss whose search was most of the file, or whose range was out of bounds, now spells out that form, and start_line/end_line sent as numeric strings are accepted. Every team whose agents hold edit_file runs on a different tool contract from this change.
  • "Finding Files" only where it applies. The locate-before-you-read section of an agent's system prompt is emitted only for agents that hold search_files.
  • edit_file takes search, not find. The old argument name is still accepted as an alias. Every role that holds list_files also holds search_files and todo now, so teams built from the shipped roles run on a new harness from this change.
  • Shipped templates are presets. The thirteen templates are written as a recipe plus nodes that name a role_id; the full team is expanded when the template loads, and each preset resolves to exactly the team its full-form predecessor did (a golden test pins it). Both forms remain valid for user templates.
  • Shipped templates list their full tool sets. Seven templates were silently widened on create (reply for workers, delegate and the planner tools for supervisors, edit_file for content and code roles); they now state those tools explicitly, and a test keeps every shipped template a fixed point of the resolver. What runs is unchanged.
  • Team creation says which tools it adds. The supervisor-wiring pass that runs on every project create logs each tool it grants with its reason. It now also grants read_plan and update_plan to a supervisor that has create_plan, and grants edit_file to content, code and test roles only when they already have write_file — a read-only role stays read-only.
  • Quick Create validates generated tool names against the tool catalog: exact names are kept, case-only mismatches normalized, unknown names dropped with a warning.
  • Quick Create no longer evicts a resident model. The team generator uses the chat model already loaded on the Primary instance, then the Secondary, and loads one beside what is there only when neither has any.
  • Completed runs are read from the session log. The trajectory endpoint now says where a run sits in the durable event log, and the run viewer folds that slice through the same fold the live viewer uses — reasoning, context decisions and step rounds included — with a header badge naming the source. Runs that predate the log still show their interaction-log rows.
  • A plan step's write must succeed to count as delivered. A step whose only write_file / edit_file calls all failed now fails the deliverable gate, re-plans, and (with rounds enabled) gets a fresh attempt. Previously the tool name alone satisfied the gate, so a step could report "completed" with nothing on disk.
  • Reasoning is no longer copied into an empty answer on tool-call turns. When a model answers with tool calls and no text, the tool calls are the answer; the reasoning stays in its own stream instead of entering the conversation history as the agent's words. Benchmark results for reasoning models recorded before and after this change are not comparable.

Fixed

  • Deleting a project now also deletes its rows in the durable session event log.
  • project-config.md is now inside the agents' workspace. It was written to the project root only, while the agents' container mounts workspace/, so the "read project-config.md first" instruction every shipped role opens with failed on every run. Both copies are written now; runs that read the file are on a new harness from this change.

2026-09-02

Added

  • Context overflow is recovered, not fatal. When the inference server rejects a request as larger than the model's window (LM Studio's exceed_context_size_error, which carries the real prompt token count), the Engine now shrinks the request by the measured overshoot — oldest middle messages first, then the longest messages elided head and tail — logs the measurement as an aetherium.context.fitted event, and retries once. Previously the error failed the step, or the whole run in a single-agent topology. The pre-flight fit check also no longer sends a request it has already measured as too large.
  • Tool output is capped by the remaining window, not a flat 4,000 characters. A single tool result may now use a quarter of what is left in the context window, never less than the old 4,000 characters and never more than 24,000, in both the tool loop and the hierarchical history — so a short conversation in a 32k window sees the whole file.
  • Compaction says why it declined. When mid-run history is summarized, a summary that would not be smaller than what it replaces is refused, and the reason (no model, empty summary, not smaller) is logged as an aetherium.context.compacted event with strategy: declined.
  • Durable session log. Every event on the AG-UI run stream is written to a new session_events table with its sequence number. GET /api/agui/{projectId}/stream and /events replay from it for anything the in-memory buffer has evicted, and across a restart; numbering continues rather than starting over. /head now reports durable, persisted and persistFailures. The aetherium.gap event now means the position is in neither the buffer nor the table.
  • Context decisions on the stream. Four events put what was only in server logs onto the same numbered, durable stream: aetherium.prompt.assembled (one per model call — message counts, token estimate, window, and the cassette key; never the prompt text), aetherium.context.fitted, aetherium.context.compacted (summarize or truncate), and aetherium.memory.injected.
  • Model-exchange cassettes describe themselves. Each recorded exchange now stores the canonical request it was keyed on, so a cassette can verify its own keys and a replay miss reports at which character the prompt diverged from the nearest recording. Aetherium:Cassette:FailOnMiss turns a miss into a failure for tests; in Replay mode the connection check and the model list are answered from the cassette, so a replay no longer depends on a live inference server.
  • Plan-path replay test. The hierarchical documentation team runs end to end through the real host from a recorded cassette with no model, in seconds; the test host runs tools on the host filesystem (Aetherium:Workspace:Containers=off, for that host only).

Fixed

  • One run identity per build run. Tool calls previously got a fresh run identity each, so the write_file guards refused every retry instead of allowing the second attempt, and revert_file could not find the snapshot write_file had taken. Both now behave as documented, and the scheduler's cancellation drain matches the run's queued tasks.
  • Pre-write snapshot: absent is not unreadable. A file that exists but cannot be read at write time gets no snapshot, instead of an error message stored as its content; a file whose first line begins with Error: is no longer treated as missing.

2026-08-31

Added

  • write_file now refuses a rewrite that would damage the file. A shrink guard refuses a write that would discard most of an existing file (measured as the share of existing content, by character, that survives — below 0.5 is refused; files under 400 bytes are not protected). A reference guard refuses an HTML write that would leave a sibling script reaching for element ids the new markup no longer declares, counting only ids that were alive before the write. Both refuse once, naming what would be lost and pointing at edit_file, then allow a second attempt so a deliberate rewrite is never blocked; both fail open if the check itself errors.
  • GET /api/model-registry/status — reports why the registry looks the way it does: Loaded, NotConfigured, Missing or Invalid, with the resolved path and model count. POST /api/model-registry/reload now reports what actually happened instead of always claiming success.
  • Inferred plan dependencies. When a plan step's description names a file that an earlier step's description also names, the later step is made to depend on the earlier one. Path-shaped tokens only, and edges only point backwards.
  • Reasoning is separated from the answer at the model boundary, so a thinking model's <think> block no longer flows into tool arguments, history or written files.
  • Model-exchange cassettes (Aetherium:Cassette:Mode, Aetherium:Cassette:Path) — record a run's model exchanges and replay them with no GPU, for deterministic tests.

Changed

  • A configured-but-missing model registry is now fatal at startup. Previously it logged a warning and substituted an empty registry, so every routing decision reported as benchmark-driven was silently a fallback heuristic. No registry configured at all stays loud but survivable, since that is a legitimate way to run the Engine; Aetherium:ModelRegistry:FailOnMissing overrides in both directions. A registry that loads with zero models is warned about separately.
  • Failed step verifications are reported in the plan summary, marked according to whether the verdict came from a deterministic artifact check or from the judge. Reporting only — a failed verification does not block a step's dependents.
  • Plan dependency graphs are validated before the plan is stored. Edges naming a step that is not in the plan, self-references, and edges that would close a cycle are dropped with a logged reason rather than rejecting the plan. Such edges previously made their step permanently unreachable, silently.
  • Plan step transitions are checked. failed → completed is refused, pointing at the real route (set in_progress, redo the work, then complete). failed → skipped is allowed with a warning.
  • The per-session tool allowlist is derived from the tool registry rather than restated as a literal. The literal had drifted to 26 of 31 tools, so agents that legitimately declared read_plan, revert_file, read_logs, http_health_check or send_message_to were refused them.

Fixed

  • A truncated model response now says so. When a response was cut off by the token limit mid-tool-call, the call silently vanished and the agent appeared to refuse the work. The agent is now told its output was truncated, that nothing was written, and to build the file in smaller pieces.
  • Team saves no longer blank the project name or reset its tier — team edits apply as a patch instead of replacing the whole row.
  • The written path is recorded by argument name, not position, so a step's reported artifact is the path rather than whatever occupied that slot.

2026-08-30

Added

  • A new web client at /app — the Engine's interface was rebuilt as a React single-page app, served from the same host, and / now redirects to it. Fifteen screens including a live run viewer, a replayable trajectory viewer, a React Flow team designer, a real approvals queue, and a live team monitor. Everything that belongs to one project is reached from a bar at the top of that project's screens.
  • Typed tool-result cards — a tool result now carries a typed view alongside its text (diff, terminal, read, search, web, generic), so a file change renders as a real diff and a shell command shows its own exit code. Previously every tool result was one flattened string that the interface sorted into three visual treatments by matching substrings in a free-text label.
  • A replayable run event stream at /api/agui/{projectId}/stream — server-sent events, numbered so a client that connects late or reconnects can replay from its last position and prove it has the whole trajectory. A position already evicted from the buffer is announced as a gap rather than silently skipped.
  • /api/capabilities, /api/capabilities/cards and /api/capabilities/live — the Engine now publishes what it can do: registered tools with their schemas, the card kinds it produces, and every real-time event with its payload type. Startup and test-time guards fail if the published catalog and the code disagree in either direction.
  • The approval queue is answerable from the interface. GET /api/tools/approval/pending now returns each blocked call in full — tool, arguments verbatim, agent, security level, and a countdown to auto-deny — where it previously returned only a count, which no interface could act on.
  • Run summaries and trajectories at /api/orchestrator/runs/{projectId}/summaries and /trajectory, and team status at /api/projects/{projectId}/team/status.
  • PATCH /api/projects/{id}/team — merge a team edit while preserving fields the patch does not mention, instead of replacing the whole configuration. Every write records a version, so a mistaken save stays recoverable.

Changed

  • Run cancellation actually cancels. The stop control had been a stub that only reset the interface's own state.
  • The Aetherium.Monitor console renders a live run from the event stream alone, as a standing check that the contract is complete enough for a second client.

Fixed

  • A shell command's exit code survives to the interface. The executor had the code, the tool layer stringified it, and a parser read it back out — appending it only when it was non-zero, so a successful command's 0 was indistinguishable from "never captured", and any output containing the literal text Exit code: was misread.
  • Restoring a team version now writes the team file. The restore reported success and returned the right configuration while leaving the file on disk untouched.
  • Workspace tier values are written in the form the Engine matches. A project saved from the interface could be written as temp where retention policy matches Temp, leaving it enrolled in no policy at all.
  • A fresh Linux clone builds with no preparatory step. The first build used to fail with a glob-expansion error until bin/ and obj/ were created by hand.

Removed

  • The Blazor client. Retired after the replacement reached parity, along with the server-side render-mode plumbing it required. It is archived, not discarded.

2026-08-14

Added

  • Stale benchmark-mode auto-restore — a new background guardian heals an AI_BenchmarkMode=true left behind by a benchmark chain that died before its explicit-false restore (previously a stuck flag silently suppressed lessons, summarization, background research, and multi-instance routing for every subsequent normal run, and the only mitigation was procedural). While the flag is set, the guardian tracks build-run activity; after a configurable idle window (AI_BenchmarkModeStaleMinutes, default 90) with nothing queued, running, or recently finished, it writes explicit false back and logs a [BENCHMARK-MODE] STALE warning. Runs stuck in a queued/running state for over six hours are treated as orphaned rather than live, so the wreckage of a killed chain cannot hold its own flag hostage.

Fixed

  • Duplicate model instances no longer stack in VRAM — model loads are now serialized per LM Studio instance, and a load already in flight (state loading, including one started outside the Engine) is awaited instead of re-requested. Previously a concurrent or config-mismatched load request made LM Studio spawn a second copy of the same model under a suffixed instance id (model-key:2), silently doubling its VRAM footprint. Loaded-state checks now also recognize suffixed instances, and post-load verification unloads any stacked duplicate it finds.

2026-08-10

Added

  • Two-layer step verification (opt-in) — deterministic artifact checks after each file write (exists, non-empty, JSON parses), plus an LLM verifier at step boundaries that judges the step goal against the worker's report and the files actually written, bouncing a FAIL back to the authoring worker for a bounded number of fix cycles. Configured per team via a verification block (enabled, scope, artifact_checks, max_retries, score_threshold, verifier_model_id, …) or globally via AI_VerificationEnabled, with AI_VerifierModelId (falls back to AI_SupervisorModelId → AI_ModelId) and AI_MaxVerificationRetries. Off by default; fail-closed on settings errors.
  • Configurable self-maintenance source mount — new Aetherium:Maintenance section (AetheriumSourcePath, env-expandable; ReadOnly) replaces a hardcoded host path for the /aetherium-src mount used by maintenance templates. A blank or unresolved path now skips the mount with a warning instead of guessing.

Changed

  • Tool schemas now match the runtime allowlist — the schema set shown to a model is intersected with the node's explicit tools allowlist when one is declared, so agents are never shown tools the runtime would reject (delegate/reply stay visible to supervisors).
  • Template refresh — the fullstack-web supervisor now runs the same devstral-small-2-24b-instruct-2512 as its workers, and the fullstack-web, maintenance-team, and quick-fix templates ship with verification enabled and adjusted tool-call budgets.

Fixed

  • Tool-approval webhooks — tool.approval.requested events are now included in the default webhook target's event filter, so external approval flows receive them without manual target edits.

2026-07-20

Added

  • Roundtable multi-round discussion — new team-config key discussion_rounds (default 2; 1 keeps the legacy single round). Round one collects every agent's independent answer in parallel; later rounds show each agent its peers' previous-round responses to critique and reconcile, and the final round doubles as consolidation of the shared workspace artifact. An agent whose round fails drops out of the rest of the discussion; if every agent fails, the build fails.

Changed

  • Sequential stages keep the original task — every stage now receives the user's original task as its message, with the previous stage's output injected once as a labeled context block (replacing output-chaining). A failed stage aborts the pipeline with a failed-at-stage-N status instead of letting later stages run on stale input.
  • Topology turn budgets unified — Roundtable and Sequential tool loops now draw their per-agent turn budget from the circuit breaker's max_turns_per_agent (previously a fixed 20), matching Hierarchical.
  • Supervisor planning grounded harder against the workspace — plan steps carry explicit file-placement rules (root-default placement, declared-path guard), verify-only steps are exempt from the deliverable gate, and delegation briefs use honest empty-scaffold grounding plus semantic file identifiers.

Fixed

  • Requests are trimmed to fit the model's context window before dispatch — an oversized message set is reduced by a request-fit guard instead of overflowing the loaded model's context, and planning no longer invents server code for static-only projects.
  • Strict chat templates no longer reject topology context — Sequential's previous-stage block and Roundtable's discussion context are folded into the agent's system prompt rather than sent as a second system message, which models with strict chat templates (e.g. the qwen3.5 family) refuse.

2026-07-02

Fixed

  • Plan steps can no longer "succeed" by exploring — a step whose agent only ran read-only shell commands (ls, grep, find, …) is now marked failed with a "produced no deliverable" reason instead of passing silently. Only file writes/edits or state-changing commands count as delivered work.

Changed

  • Delegated workers receive a workspace file tree — every delegation instruction now carries a capped snapshot of the project workspace, so a worker locates existing files by exact path instead of guessing a role-implied subdirectory.
  • Read-only shell commands count toward the exploration guard — the tool loop's read-only streak now includes ls/grep-style shell calls (write-now nudge after five, forced exit after eight); state-changing shell commands reset the streak.

2026-07-01

Added

  • fullstack-web template: container init commands — the template now pre-installs the native-module build toolchain (python3, make, g++) and the serve/ http-server static servers when the project container is created, so packages like better-sqlite3 compile on the slim base image.

Fixed

  • qwen3.6 over-thinking suppressed for real — the qwen3.6 GGUF chat template ignores the enable_thinking=false kwarg, so the Engine now appends the family's /no_think soft-switch to the outgoing prompt for qwen3.6 models only. Dense qwen3.6 builds drop from >28-minute over-think aborts to normal latency.
  • Template container_init_commands run as root — init commands previously inherited the non-root workspace user, so package installs to /usr failed silently; they now execute as root while agent commands stay non-root.
  • Persistent containers survive tier-retention cleanup — projects whose team sets persistent_container: true are skipped by the hourly workspace sweep; their containers are managed solely by the container lifecycle's grace-period reaper.
  • Tool-output growth in agent context — tool results replayed from conversation history into an agent's context are capped at 4,000 characters (matching the in-loop cap), preventing context-window saturation on long tool sessions.

[Unreleased]

Added

  • Agent reflection (self-critique) can be enabled globally via the AI_ReflectionEnabled toggle, not only per agent node — it runs on conversational plan steps and in the Roundtable, Sequential, and Single topologies. Off by default.
  • Trajectory evaluation — an LLM-as-judge scores a completed run's trajectory (tool choice, plan compliance, hallucinated dependencies, deliverable presence, efficiency) via POST /api/trajectoryeval/run/{id}. POST /api/trajectoryeval/promote gates a template's promotion to core on the score (AI_PromotionScoreThreshold, default 7); the judge model comes from AI_TrajectoryJudgeModelId.
  • Parallel plan-step execution — independent plan steps now run concurrently in waves, gated by the AI_ParallelPlanSteps setting (with AI_MaxParallelPlanSteps bounding the wave width).
  • Per-instance scheduler concurrency for LM Studio, with model-affinity and VRAM-aware routing across the Primary/Secondary instances.
  • Layer 2 web-research flow restored — autonomous knowledge-gap research, now driven by schema-constrained model output.
  • /api/health/detailed endpoint, backed by an embedding-health background check.

Changed

  • RAG context quality — a relevance floor on auto-injected context, and removal of build-log self-feed, to stop low-quality context loops.
  • Circuit breaker — completed work is surfaced alongside a breaker stop, and loop-detection state resets on each new user message (no cross-message bleed).

Fixed

  • Breaker-terminated build runs now finalize as Failed, not Completed.
  • search_knowledge distinguishes an infrastructure failure from genuinely empty results, ending pointless re-search loops.
  • Embedding-space mismatch is detected, preventing silent search breakage after a model swap.
  • qwen3.6 GGUFs: enable_thinking is correctly forced off (the kwarg is a no-op for that family).