v0.1.19 — the guide survives a tool call
An audit of the shell through an LLM-engineering and an SLM-architecture lens turned up seven findings that were fixable today. All seven are here.
The skill guide no longer vanishes after the first tool result
<guidance> was written only inside the question turn — and from the second turn of the tool loop onward the question is deliberately left empty, so the guidance disappeared at exactly the turn where the model is recovering from a tool result and needs it most.
Plain kept it, which is worse than a plain bug: eval runs on FakeEngine, FakeEngine is Plain, so the one template no model ever sees was the one template the measurement covered. ChatML now carries the guidance along with the last <tool_response> rather than opening a second consecutive user fence; Gemma pushes a block and lets the existing merge fold it.
A turn that runs out of budget no longer does it silently
If the model called a tool on all four passes, the shell used to leave the answer empty, print nothing, store no assistant turn, report "error": null and exit 0.
(stopped after 4 tool turns without an answer — try asking for one step at a time)
No answer is invented. The tools that ran are already on screen as chips; what was missing was that it had stopped. It also exits non-zero now, for the same reason an engine error does: the caller of tacet -m ... --json checks the exit code.
--json keeps the contract its own documentation described
error serialised to null on a clean turn, so the field was always present and the documented has("error") check was true for every line the shell ever printed. It is absent on success now.
create_document told the model "do note ask or narrate"
Where it meant "do not ask". One garbled word, in a sentence the model reads on every turn that tool is selected.
New: --temperature and --seed
tacet chat --temperature 0.7 --seed 42
tacet config set temperature 0.7
The defaults do not move — greedy, seed 0, locked by a test. But until now the variance of this shell could not be measured (every run took the same single path through the model), and a user handed a bad greedy answer had no second one to ask for. A session running off the default says so on the banner, for the same reason EngineIdentity exists: a run that cannot be reproduced must not look like one that can.
A ceiling under the tool descriptions
They are the largest block in the prompt and until now had none — every other injected text here has a documented cap.
Writing the test corrected the estimate that prompted it. The worst nine-tool selection is 2542 tokens, not the ~1850 expected: on the 4096-token floor window that is 62% of the window before the system block, the memory, the guide, the history or the question have been written — and it is re-sent on every pass of the tool loop. The test is a ratchet at today's number, not a demand to shrink it.
Also
Two stale comments corrected. thinking.rs claimed a first line of defence in prompt.rs that was tried and reverted — it turned thinking off and broke the tool-call format with it (Qwen3-4B 7/10 → 2/10). router.rs still warned that a message written in Turkish touches no trigger, a price locale_triggers stopped charging some releases ago.
Not in this release
The audit's larger findings stay open, and they are worth naming: the tool loop re-prefills the whole prompt on every pass — one user message measured 12250 prompt tokens across four turns — and the real-model eval still scores tool selection only, so nothing measures whether the final sentence is right.
Crates
tacet-cli 0.1.19 · tacet-tools 0.1.9 · tacet-engine 0.1.5
tacet-engine 0.1.5 and tacet-tools 0.1.9 are floors, not preferences; the rationale for each sits beside its pin in the workspace manifest.