Nothing changes silently.
A diagnostic tool must not change behaviour silently: the same log through two versions can produce different reports, and you deserve to know whether that difference is us or your robot. Every entry answers one question: what will read differently after this upgrade. Every diagnosis records the engine that produced it (measured_with in the JSON), so an old report never has to be guessed about.
Live camera context in pilot builds
- Live camera context in the current pilot watcher can send selected recent frames to the configured model during an investigation. It can be disabled per robot or with
trialone watch --no-camera. It is separate from raw recording uploads and does not act as an image fault detector. This feature is not included in 0.4.21.
0.4.21
30 September 2026
Say what happened, from your phone, and get the answer there
- Open
trialone.dev/m, or press "Something happened" at the top of any page, tap the microphone and say what you see. The watcher on the robot looks at the 30 seconds before the moment you pressed, not the moment the report arrived, and answers on the same page: a verdict with the repair, the one test that decides it, or that nothing is wrong.trialone mark it stopped at the dockcarries your words the same way. - The watcher now takes any number of marks in one session. Before, it took the first and ignored every later one.
- At a mark, the investigator now runs by default on a live ROS 2 graph when a model is configured: it reads the graph, the parameters, the command lines, the clocks and the devices' own reports itself, and a verdict leaves only when its predictions hold on the robot.
TRIALONE_ERMITTLER=0turns it off. Without a model or a graph, the answer is the timeline of what the instruments saw.
What changed on the robot, whenever you ask
- While it runs, the watcher records every change on the live graph every 20 seconds: nodes that appeared or went away, who publishes and subscribes what, processes that restarted, parameters that changed, and new warnings the devices report. Each change is printed as it happens,
trialone changes --since 2hlists them on the robot without a network, and with the machine state atfull(the default) they appear on your account next to a report and on the machine page. No model is involved.
The live watcher decides: a verdict or the one test, never a hint
- Every work order from
trialone watchnow carries a verdict or the single test that decides it. A silent device is decided by asking the process behind it (ended, stopped, or its device removed), shared timing trouble by the kernel's pressure figures, and a blocked joint by one test with three outcomes. Findings that only stand out no longer leave the watcher; the summary counts them. - A robot that is commanded to move and does not, while its joints push as hard as when walking, gets the verdict that something holds it.
- A joint that never follows its command is now found on the live graph and in a recording, paired with its command by joint name.
The watcher reads lidars and point clouds
sensor_msgs/PointCloud2and LivoxCustomMsgtopics were skipped as bulk data, so a lidar was invisible to the watcher. Each cloud now becomes a few channels: the number of points, the share of valid returns, the nearest range and the largest plane. A return without an echo is not counted as a point.- A laser scanner that keeps publishing but has lost every valid beam now gets its own test instead of a silent device.
The watcher keeps hearing its robot under load, and on graphs with two clocks
- On a busy computer the watcher itself could fall behind and report healthy nodes as silent. It now receives in its own thread and never reports its own gap as the robot's.
- A graph that stamps some topics in sensor time and others in ROS time left the watcher without data. Each topic is now placed on the time it arrived when its stamps are far off, and the watcher says so once. Two publishers on one topic are no longer mistaken for a clock that jumped back.
- When a publisher switches to best effort after the watcher subscribed, the watcher subscribes again instead of going deaf.
- The loop no longer slows down the longer it runs: it reads only the window it examines.
Recordings
- A bag with one message type TrialONE cannot decode no longer loses every other topic. Before, such a bag was read as empty and the report said no fault was found; now the standard topics are read, the others are named, and a file with nothing readable ends with a sentence saying so.
- Parquet tables are read like a CSV export.
- A console log with a failed assertion between ordinary ROS lines is no longer reported as having no fault.
Answers from the model are not lost
- A backslash in one of the model's search patterns made its whole answer disappear, and an answer that arrived cut off or as Markdown was dropped. These answers now arrive. On a crash with a source tree, the report names the main file to change first, chosen from the files the model actually read.
A recording now shows what the stack reported about itself on /rosout
- When a publisher and a subscriber cannot agree on QoS, for example a driver publishing best effort and a consumer requiring reliable, ROS drops the connection and nothing arrives, without a crash. Both sides say so on
/rosout. TrialONE already read these lines in a console log, but in a recording it only counted how many warnings arrived per second. On a recording where the affected topic itself looked healthy, the report said no fault was found. Such a recording now gets a finding for each affected topic, with the policy and the repair, and that finding leads the report. - The same rules that read a console log now read
/rosoutin a recording, so the other warnings they decode, such as a frozen transform or a control loop missing its rate, reach a recording as well. - Only warnings and errors in
/rosoutare read, and a warning that repeats in a loop is read at most 50 times. On a recording with more than 100,000 informational lines this keeps the added time near one second, and a warning that appears after a flood of repeats is still found.
The watcher reports mismatched QoS before any node warns
trialone watchnow compares the QoS of the publishers and subscribers of every topic on the live graph, using the compatibility rules from the ROS 2 documentation: reliability, durability, deadline and liveliness. A pair that cannot connect becomes a finding with both node names and the repair for that policy. Settings that are only decided at connection time, such as system default, are not judged.
A CI step can stop on a finding
trialone diagnose --fail-on faultends with exit code 4 when the recording carries something that would wake a person,--fail-on findingon every finding that stands on its own,--fail-on anyon everything the report names. Without the flag the exit code stays 0, findings or not, as before.--summary-mdappends a short Markdown table of the findings to a file, for$GITHUB_STEP_SUMMARYorgh pr comment --body-file.
A running watcher keeps what people wrote
- Notes for the whole site, comments on an issue and repair notes reached a watcher only in its first minute. It replaces its list of notes every minute from the same route that hands it jobs, and that route delivered only the notes of this one machine. Both routes now deliver the same list, so a note written at 10:12 is in the interpretation of 10:13 and stays there.
The watcher's service stops after five failed starts, as its own comment promised
trialone install-serviceanddeploy/trialone-watch.servicewrote the restart limit into the[Service]section, where systemd ignores it (measured with systemd 255). A watcher that could not start restarted every 30 seconds forever instead of stopping. Re-runtrialone install-service --installto get the corrected unit.
Note what you saw at a second of the recording
- On the incident page, click the time axis under the curve, or use the button below it, to write down what you saw at that exact second: "the gripper was tilted before it closed". The note appears as a dotted line across every channel, and the next diagnosis of that machine, the running watcher and Ask all read it with its second.
A decoded repair now reaches the incident page and your coding agent
- When a finding comes from a rule that knows its repair, such as mismatched QoS, the repair was printed in the terminal but did not travel with the finding. The incident page answered "No repair proposed by this run", and a coding agent fetching the issue did not get it either. It now travels as the reference pattern, and it shows up under Fix. When this machine has its own history for the same fault, that history still comes first.
Older recordings get the self-report, camera and /rosout checks
- A rosbag2 recording without embedded message definitions, which includes many recordings made with Humble, could not be opened by the checks that read
/diagnostics, the camera topics and/rosout. They reported that the file was not a recording they could open. They now open it the same way the rest of TrialONE does.
Local case history
- TrialONE can link findings, reported changes and follow-up recordings for a named robot.
trialone repair <diagnosis.json> --finding <finding-id> --change "Applied change"records a change your team already made; it does not modify the robot.trialone verify <diagnosis.json> <new recording> --attempt <repair-id>links the follow-up to that attempt, andtrialone cases --machine <robot-name>shows the local history. Passing, failing and inconclusive follow-ups are retained. Available since 0.4.21.
0.4.20
17 September 2026
The install command now finds the packages TrialONE depends on
- The command in 0.4.19, on the website and in the docs, pointed pip at our package index only. That index holds TrialONE and nothing else, so pip stopped at the first dependency with
No matching distribution found for click. Every install command now names PyPI as a second index for the open-source libraries:pip install --index-url https://trialone:<your-cli-key>@trialone.dev/api/pypi/simple --extra-index-url https://pypi.org/simple trialone. TrialONE itself still comes only from our index. The manual command thattrialone updateprints when it cannot update by itself carries the same fix.
Your robot's own health report stands next to our finding
- When a recording carries
/diagnostics, the ROS standard for robot health, the report now says what the machine reported about itself during that recording, and whether any of it was new. A monitor that was already complaining in the first message is a standing condition and not an event of this run, and it is reported as one: a battery that warns from the first second of a healthy flight does not explain a motor failure later on. The field isself_reportin--json, and the incident page shows it.
Duplicate recordings no longer count twice in a healthy reference
trialone learnskips a file that duplicates one it already read (same size, same first and last megabyte) and names both. Before, a copied recording raised the number of operations without adding any spread, and that number feeds the false-alarm rate that decides between FAULT and CANDIDATE.
Same code, different machine
- The machine state now records the processor and the architecture. When a run has findings, the report says what changed since the last run on this machine: the commit, the software, and devices that disappeared or appeared. When the commit is identical and only the processor differs, that is said first, because an engineer would otherwise search the code for a fault that lives in the build or the hardware.
A frozen camera is now visible
- A camera driver can keep publishing at full rate after its image stopped changing, so silence and rate checks never see it. For compressed image topics, TrialONE now reports 30 or more messages in a row with exactly the same size. On a live camera we measured at most 2 in a row, on colour and on depth, and the report carries that number. It is a LOOK and not a FAULT: a robot waiting in front of a blank wall produces the same pattern. Raw images (
sensor_msgs/Image) always have the same size and are never flagged. The field isfrozen_camera.
Crash stacks from backward-cpp are read as crashes
- Stacks that count down to
#0("most recent call last"), and stacks carrying the prefix thatros2 launchor colcon writes in front of every line, went through the text lane before and ended in "no known crash signature found". They are now decoded as crash stacks, with their frames, and with files and lines where the stack names them.
The QoS of each recorded topic goes into the recipe
- A rosbag2 recording stores the QoS each publisher offered. The recipe now carries it (
transport), and next to a finding the topics offered with anything other than the default go into the task for the model. This is context and never a finding: a recording holds only the publisher side, and a mismatch needs the subscriber side as well. Older rosbag2 versions that store these settings as numbers are decoded.
0.4.19
16 September 2026
A long tool-call argument no longer stalls a report
- While writing the provenance block ("what the layer looked at"), TrialONE scanned every tool-call argument with a pattern that needed quadratic time. One very long argument, of the kind a model can write, could hold up the end of a diagnosis on your machine: 20,000 characters took over three seconds here, 40,000 over twelve. The scan is now linear, and 200,000 characters take under a millisecond. On everything a tool actually passes, it finds the same files as before, so no report reads differently.
- This was reported to us privately from outside the company. It could cost time on the machine running
trialone; no data left it, and no diagnosis changes because of it.
TrialONE now installs from our own package index, not from public PyPI
- Using TrialONE always needed an approved account; reading its source did not, because the package sat on public PyPI. From this version the wheel lives behind the same key your robot pushes with. Install it with your CLI key from Settings, CLI keys:
pip install --index-url https://trialone:<your-cli-key>@trialone.dev/api/pypi/simple trialone, and sign the machine in with the same key.trialone updateuses that index by itself; without a sign-in it now says so instead of reaching for PyPI.
The published package no longer carries our internal comments
trialoneis a pure Python package, so whoever installs it reads the source. From this version the wheel is built without our comments, which were a third of it. Docstrings stay, because the layer reads its tool descriptions from them and--helpis built from them; line numbers stay, so an error from an installed copy still points at the same line we see. Nothing about behaviour changes.
0.4.18
15 September 2026
The welcome shows the TrialONE mark
- When
trialoneopens in a window with room for it, the welcome shows the TrialONE mark, the same one as on trialone.dev, and next to it the name, the version and one sentence on what it does. The robot drawing and the name in block letters are gone. The name is plain text now, so a screen reader or a search through a saved session finds it. - In a short or narrow window, or where the console cannot draw block characters (or with
TRIALONE_ASCII=1), the welcome stays one line, as before.
0.4.17
15 September 2026
Signing in reads cleanly in every terminal
- In cmd and other Windows consoles,
trialone loginshowed raw control codes such as←[1maround the address and the code. Every command now switches the console to show color before it prints, and prints plain text where the console cannot.NO_COLORnow also applies to the lines that ignored it until now. trialone loginshows the one-time code first and opens trialone.dev/cli-login when you press Enter, so the browser no longer covers the terminal you read the code from. On a robot over SSH, in a script, or with--no-browser, it prints the address and waits, as before.- After signing in, it names the file the key is stored in and where to revoke it.
0.4.16
15 September 2026
Signing in no longer means copying a key
trialone loginwithout a key shows a short code and the address trialone.dev/cli-login. Enter the code there while you are signed in and approve it: the terminal receives its key within seconds, and the key is checked before it is saved, as before. On a robot over SSH, open the page on your laptop;--no-browseronly prints the address. The page never takes the code from a link, so a link someone sends you cannot sign their terminal into your account.trialone login <key>andtrialone login --pastestill take a key from Settings, CLI keys, for scripts that hand out keys.
One sentence in the report reads differently
- When far more operations of a set cross your healthy reference than its false-alarm rate allows, the summary, the finding, the recipe and the next step now name both readings: this set differs from the reference (a later session, a different speed or policy, a changed task), or something on the machine changed since the reference was recorded. They say what decides between the two. Until 0.4.15 they named only the first and suggested adding such runs to the reference, which for a part that jams in every operation would have built the fault into it. The classification (CANDIDATE, zone LOOK) is unchanged.
- The probability in that sentence reads "less than 1 in a billion" instead of a number like "1 in 999,999,999,999,999,872".
0.4.15
15 September 2026
Two weeks of changes. Read the first block before you upgrade a robot: more of what a run measured now leaves the machine by default, and you decide per machine how much.
TrialONE now runs only for an approved account
- Every command that starts the engine (
diagnose,watch,verify,learn, the window you get fromtrialonealone, and the rest) first checks that this terminal is signed in withtrialone loginand that the account is approved. Access is by request at trialone.dev/request-access.login,whoami,updateand--helpwork without it. - The check asks trialone.dev at most once a day. Without a network, the last confirmation counts for 30 days, so a robot in a hall without Wi-Fi keeps running. Refused: exit code 77.
- Help, error messages and the update notice now point to the documentation at trialone.dev/docs and to this changelog at trialone.dev/changelog.
trialone loginnames the page where CLI keys are created (Settings, CLI keys).
What leaves the robot now: by default, and how to turn it down
- History, level
fullby default. A pushed run now carries a trend of every channel that moved (min/mean/max, 120 points per run; while watching, one point per minute), the minute around each finding (30 s before to 30 s after, 10 points per second), and a packed file with every moving channel at 10 Hz, uploaded to our storage. Up to 0.4.14 only per-channel statistics left the machine. Turn it down per machine under Settings → Robots, or with--history off|incident|full; a flag wins over the setting, and the screen says which applied. - Machine state, level
fullby default. A run records the git commit and the last 50 commit subjects of--project(hash, date, subject: no source code, no diff, no credentials from the remote), and the parameter, launch and URDF files next to it, contents sent once per hash.--state off|code|full, or per machine in the settings. - The recording itself: never, unless you switch it on for a machine or pass
--upload-raw.
New commands, and a watcher that finally talks to the website
trialone learn <folder>reads recordings the robot already made as the reference for the live watcher. No model call, no upload. It also reads a rosbag2 folder whosemetadata.yamlis missing, if it holds MCAP: that is what a recording cut off by a crash looks like.trialone install-service --installinstalls the watcher as a user service (no sudo), starts it, and checks that it actually stays up instead of restarting every 30 s.trialone attachrecords what this robot is, with a date.trialone mark <recording>says "something just happened" about a recording.trialone push <diagnosis.json>sends a diagnosis that is already on disk to your account, without analysing again: for a run whose push failed. Exit code 3 if it still does not arrive.trialone watchpushes when you are logged in, likediagnose(--no-pushto stop). Until now it pushed only with--push, and the service file from our own pilot guide did not pass it: a watcher set up exactly as we described sent nothing. A logged-in watcher now also checks in every minute (the website can tell "silent" from "stopped by hand"), takes jobs from the website (build a reference, verify a repair), and says goodbye when it is stopped.- New flags:
--history,--state,--upload-raw,--no-upload-rawand--no-pushondiagnoseandwatch;--healthy,--project,--incident-atand--explain-budgetonwatch;--no-pushonverify.
Teams that train policies: LeRobot datasets, and "policy or robot?"
trialone diagnose <dataset folder>reads a LeRobot dataset (v2.x and v3.0; the format LeRobot records, and the one openpi/pi0 and NVIDIA GR00T train on, including GR00T'smeta/modality.jsonfield names).trialone diagnose lerobot/pushttakes a Hugging Face dataset id: the copy LeRobot already has on this machine first, otherwise metadata and only the data files that are read. Videos are never read or downloaded, and the report names the camera streams it skipped. Needspip install "trialone[lerobot]"(pyarrow, huggingface_hub).- Each episode is one run of the task;
--healthy <training dataset>measures rollouts against the data the policy learned from.next.successis used as the reference only when the dataset holds both outcomes: onlerobot/pushtit is true in none of the 206 episodes. - Where a dataset pairs
actionandobservation.stateper joint, every flagged episode says which side it is on: POLICY, NOT THE ROBOT (the arm followed, the commands differed), THE ROBOT, NOT THE POLICY (ordinary commands, the arm did not follow), or that the two cannot be separated here. A policy finding has its own route,policy, and goes to the person who trains the policy instead of a technician or a coding agent: on the website under Fix, in the recipe aschange_class: policy, and for coding agents as routepolicyinget_issue.
The live watcher reads differently
- Three zones per window. FAULT is evidence that stands on its own (a device that went silent, a cadence shared by many channels breaking together, a sibling pair moving in opposite directions, a reference with a measured false-alarm rate). LOOK is everything else that fired: it comes with something to watch instead of a
DO:. The report says "N of M … undecided". - An all-clear from the model is no longer a veto. The task stays and is marked NOT cleared.
- It names the part, uses a learned reference, and with
--projectcan land on a file. - Silent failures that changed answers, fixed: a whole node dying was invisible; clock jumps were confused with transport reordering; the live CAN lane delivered zero channels; three live detectors received the whole 30-minute buffer instead of their window, so a jam got harder to see the longer the watcher ran; a machine standing still could acquit a jammed joint.
Verdicts and tables that read differently
trialone verifyno longer says FAIL when the original findings are gone and all that is new are CANDIDATEs (measured without a false-alarm rate): that is now INCONCLUSIVE, exit code 2, with the reason. Measured on our own demo pair: the recording after the repair is byte for byte the healthy reference, and the old verdict was "FAIL: 1 gone, 0 still present, 4 new". A finding that is still present, or a new one that is not a mere candidate, is FAIL as before.- A table whose time column is not called
timestamp(a UR export writestime) now gets its cycle time without--time-col, and the time column is no longer measured as if it were a signal. This can change which operations of such a table rank highest; the report says which column it took. - A CSV saved by German Excel (
;between fields, decimal comma) is read as the table it is, instead of as one column. - A folder of separate MCAP recordings (a night of simulation runs, each file its own run) is no longer read as ONE recording. Parts of one rosbag2 recording share the name
<name>_<n>and follow each other in time; anything else is diagnosed file by file, andtrialone learntakes such runs one by one. Measured: three 84 s runs in one folder had come back as 21 operations instead of 63, and the jammed joint of the second file as four weak candidates without a file. - On the website, a live finding in the LOOK zone no longer sends a Slack or Discord alert, even at HIGH severity; FAULT does. The finding page now shows the zone next to the severity.
Teams on the website
- A workspace can have members. The owner invites people by email as
memberoradminunder Settings > Team; everyone in the workspace sees the same robots, issues and runs, and a robot or coding-agent key that a member creates reports into the workspace, not into a private account. The owner alone manages billing; owners and admins manage people, robots and connections. Needsweb/supabase/team.sql; without it every account works alone, as before. - Pro is sold per seat again (1 to 25). An open invite holds a seat; the owner adds or gives up seats on the Team page, charged pro rata.
- People appear with their name and picture (from their profile or their Google or GitHub sign-in), not with their email address.
- Invites arrive without anyone forwarding a link: our own email when email sending is set up, otherwise the invite email of the sign-in service (it creates the account and signs the person in), and for an address that already has an account the invite waits inside TrialONE, above every page. The screen says which way it went; the link to copy is only the fallback. An invite can be declined.
- An issue shows who is on it. Assign it to yourself or a teammate from the menu next to its state; setting In repair assigns you when nobody is. The issue list shows the person and filters by Assigned to me, Nobody yet or a person.
- Every issue keeps what people did on it, with their name: assigned, state changed (with the note), commented, asked a robot to verify, opened a GitHub issue, shared, a repair claimed through a coding agent. Comments can be written on the issue page. Needs
web/supabase/team_aktivitaet.sql; without it everything still works and the page says that the history is not recorded. - The issue page carries the connections: create a GitHub issue (afterwards a link to it), share to Slack or Discord with a line of your own, open the recording in Foxglove when one is uploaded.
- Ask answers questions about the team: who is in it and who is only invited, who is assigned to which issue and who assigned it, what someone did. Issues have no numbers: asked about "issue 4567", Ask says that no issue starts with 4567 instead of answering about another one. Ask also answers before the first run is pushed; until now it refused with "Nothing to answer from yet".
- Ask counts its daily ceiling per seat, so a team does not share the limit of one person.
- The Kind and Fix filters on the issue list take effect at once; before, the list could keep showing the previous selection until another filter changed.
Microcontrollers: Arduino, Teensy, ESP32
- An ESP32 crash report is read, not skipped. Text from ESP-IDF, the Arduino core for ESP32 and micro-ROS on ESP32 (
Guru Meditation Error,abort(),assert failed:, brownout, stack and heap faults, task watchdog) gets its own decoder. Until now a RISC-V panic was announced as "Cortex-M / NuttX-style" and came back "No decodable fault", and an Xtensa assert with a decoded backtrace came back with no finding at all. The register that names the exception is checked against the panic line; on RISC-V the refused instruction in MTVAL is decoded (a floating-point instruction with the FPU off is named as such). A brownout is routed to hardware, everything else to the coding agent. trialone watch --serial /dev/ttyACM0(orkind: serialin robot.yaml) watches a microcontroller on its serial port: lines in the Arduino Serial Plotter format (label:value) become channels, and an ESP32 that crashes while watched becomes a work order from its own report. Opening the port does not reset the board (DTR and RTS stay low). Needspip install "trialone[serial]".--followreads Serial Plotter lines; before,current:1.2,temp:40was taken for a CSV header.
Recipes carry more
- New fields:
inventory(every channel with count, rate, range and whether it moved),operations(cycle time as a number),state,history,incident_windowper finding,fault_group(the family of a finding) androute(hardware / software / parameter / combined / unknown), andt_from/t_towherever a finding has a time. - When the model ran, its next check reaches the recipe; before, runs that spent minutes in the model could still say "none defined".
- What a person wrote down on the website ("swapped the connector on the 5th") reaches the interpretation, marked as a claim and never as a measurement.
On screen
- Under a candidate, the short view shows the measured number instead of a bare "RULED OUT by this recording:" with nothing after it.
- German words left the English output ("hoch" is now "above"/"below", plus five earlier lines).
- The report header lost its empty brackets, and verify lines are no longer cut mid-sentence.
- The refusal to calibrate a false-alarm rate is in English now ("NOT CALIBRATED ...").
- Without a model, the live watcher states why there is no explanation once per session and then one short line per finding, instead of the same paragraph under every finding.
- After a diagnosis that goes to a coding agent, the screen also names the way without copying: connect the agent once on the website and ask it for the newest issue.
0.4.14
31 August 2026
Three failures reported from one live session: and the two things that session showed were missing.
/updatecould uninstall trialone and leave nothing behind. A user ran it, saw one yellow line:Uninstalling trialone-0.4.12:: and afterwardstrialonewas gone (ModuleNotFoundError). Three defects had to line up: the failure sentence was built from pip's last STDOUT line (its progress chatter) instead of its stderr, so the real reason was never shown and the Windows-lock branch could never match; nothing checked afterwards whether the package still existed; and a 300 s timeout could kill pip between the uninstall and the install, where its own rollback does not run. Now: the update is judged by the STATE afterwards (asked in a fresh process, not by scraping text), a package that pip removed and failed to restore is repaired automatically, and if even that fails you get the exact recovery command instead of a yellow aside./updatealso installs the version the window advertised, not whatever PyPI calls newest a minute later.- The window could stay silent about a new release for a whole day. The version check cached PyPI's answer for 24 h: including an answer that was already known to be wrong (it named a version older than the one installed, which is what happens when you run a hand-built wheel). That cache is now discarded when it is older than your install or was written for a different one, and the interval is 6 h instead of 24 h.
- A located fault now carries the code, not just the address. When you run with
--project, eachfile:linein the recipe brings the lines around it (13 at most, 200 characters each), and the web view shows them with the hit line marked.costmap_2d.cpp:266is the right answer and still not an action: it makes you search, open and jump. What this changes about the promise, said plainly: the recording still never leaves your machine: an excerpt is not a recording: but your SOURCE lines now travel with the recipe. The page says how many, andTRIALONE_KEIN_QUELLTEXT=1switches it off entirely. - A crash backtrace is now a located fault. Measured on
navigation2#5710without any model call: the report said "Crash site (first user frame): … at costmap_2d.cpp:266" while the recipe'sfiles and linesfield stayed a gap: the address was in the same object, one field away, and the field a coding agent reads was empty. The crash frame and its caller are now resolved in YOUR tree and stated as candidates (the line number comes from the reporter's build, so it is not claimed as proven). - HTTP 429 said nothing about why. The screen read
openrouter said HTTP 429 -- waiting 60s, then retry 6/6while the answer body said: providerDeepInfra, errorengine_overloaded, limit sourceupstream_provider_shared_pool: that is the provider's shared pool for that model is saturated, not you are asking too fast, and no amount of waiting fixes it (measured: the model in question has exactly one provider, so there is no route around it, while two other DeepSeek models answered in under two seconds). Such a 429 now retries once briefly and then stops with the provider, the reason and the way out: pick another model. An ordinary 429 keeps the old exponential backoff.
0.4.13
31 August 2026
/modelnamed the provider that does not answer. With two keys stored (say Anthropic and OpenRouter), the window resolved "which provider" by which key sits first in the table: it never looked at your explicit choice. So after switching to OpenRouter you still readprovider anthropic, and worse:/modelthen fetched Anthropic's catalogue, your pick was stored asclaude-sonnet-5, and the run went to OpenRouter, where that model is calledanthropic/claude-sonnet-5. If a report of yours says "ANSWERED BY A DIFFERENT MODEL", this is why. The window now asks the same code the engine asks, so the name on screen is the name that gets the request.- A provider chosen without a key is said out loud.
TRIALONE_LLM_PROVIDER=openrouterwith no OpenRouter key used to fall through silently to whatever other key existed./modelnow stops with the reason instead of fetching a catalogue that cannot exist. - Signing in is the switch: you no longer type
--pushevery time. Once/loginconnects an account, diagnoses in the window push by themselves;--no-pushstays as the way out for a single run. Forgetting the flag used to cost you silently: the run was missing from the history and the issue counted one occurrence less, noticed days later. - The startup screen says whether an account is connected, next to recordings, source tree and model: reading the file only, no network call, so a bad lab WLAN cannot make the start hang.
- Two new deterministic scans over your source tree, both with a denominator and no model call: which sibling class breaks a contract its family keeps (a missing guard, a base class without a virtual destructor), and which file breaks the naming convention the rest of your tree follows. They claim no fault: an outlier can be deliberate: and they are put in front of the interpreting layer as data rather than offered as a tool it may ignore.
- The report now states what the layer was TOLD before it answered, read off the prompt that was actually sent. Without that line, "it was told and discarded it" and "it never saw it" look identical in a report, and those are two different repairs.
0.4.12
30 August 2026
- Resizing the window no longer leaves a second, dead input field on screen. The wipe now waits until the size stops changing, clears, and clears once more a beat later: the second pass catches whatever repainted in between (the first attempt raced prompt_toolkit's own resize redraw and lost by one frame). One field, always. The honest price: what stood above the field is gone after the resize (it would otherwise be a torn copy of itself).
- A version-check cache entry with a future timestamp can no longer pin the red update line forever, and a successful
/updateclears the cache so the next start asks reality.
0.4.11
30 August 2026
Fixes from the first live update drill (three window bugs, all reported with screenshots):
- The update notice lives IN the prompt now and is redrawn with it: in 0.4.9/0.4.10 it was printed from a background thread and immediately painted over, so nobody ever saw it. It clears itself once
/updatesucceeds. - The logo no longer disappears in normal-height terminals with many recordings: the recording list shrinks first (down to three rows,
/lshas the rest), the logo falls only after. - The version is part of the banner itself, in both sizes: no more duplicate
v…line.
Known limit, named rather than hidden: minimizing/restoring a maximized Windows console can leave a stale copy of the input area in the scrollback. That copy is dead history the moment you type: the live field at the bottom is the real one.
0.4.10
30 August 2026
Nothing reads differently. This release exists to prove the announcement chain end to end: it was published, and every 0.4.9 window announced it in red within a day: the first release that could not happen silently.
0.4.9
30 August 2026
New: the window keeps you current: without ever updating itself silently.
- The window shows its version under the banner (
v0.4.9): the first question of every support conversation, answered before it is asked. - On startup the window checks PyPI in the background (at most once a day, 3-second timeout, silent when offline;
TRIALONE_KEINE_VERSIONSPRUEFUNG=1turns it off). If a newer version exists, ONE red line names it, with the fix beside it: type/update. /update(window) andtrialone update(terminal) fetch the newest version in one step: always on your command, never automatically: a diagnostic tool that rebuilds itself between two diagnoses would change answers without consent. After updating, restart the window; the running session keeps its old code and says so.- This changelog ships beside the wheel, and a release without an entry here refuses to build.
0.4.8
30 August 2026
New: your terminal can hand results to your account.
trialone login(also/loginin the window) connects to trialone.dev: the key is checked against the site before it is stored.--pushondiagnoseandverifyposts the nine-field recipe to your account and prints a private link. Only the recipe travels (a few KB, including the log lines it cites as evidence); the recording never leaves your machine. A dead network is one sentence, never a failed diagnosis.- Every diagnosis now carries a
fingerprint: the group identity that lets the website say "this is the same fault as last Tuesday, ×3" instead of listing three unrelated runs. Built only from deterministic finding identities, never from model prose.
Reads differently after upgrading:
- Crash reports from stripped release binaries now name the true crash site (frame 0) instead of the first frame that happened to carry a line number: previously that could point ten frames deep into framework code (tf2, rclcpp) that has nothing to fix.
- The caller chain under a crash follows the stack of the crashing thread; it no longer mixes frames from other threads of a
thread apply all backtrace. - Plain-text reports of a crash ("Segmentation fault in X::y" in an issue text or console log) are diagnosed; previously some produced a two-line report and stopped.
- Start scripts (
rc.*, the PX4/NuttX convention) are visible to the source search and quoted as evidence: a wrongly configured driver flag is now findable where it is set. - File paths in reports resolve the way people write them (
amcl_node.cpp,nav2_amcl/src/amcl_node.cpp, from the workspace root): the report no longer claims your own file "does not exist in the project" because of a path prefix.
0.4.7
29 August 2026
- First release on PyPI:
pip install trialone. - Fixed: thinking models (Claude Sonnet/Opus) received a token budget too small to answer: a paid model call could return nothing. If 0.4.6 gave you empty model explanations, this was why.
- Diagnoses run at temperature 0: the same log and the same tree now produce the same answer.
Pre-PyPI, distributed by hand as wheel files. Reports from these versions carry their version in measured_with.