Metadata-Version: 2.4
Name: trialone
Version: 0.4.13
Summary: Robot fault diagnosis: a log in, what is wrong and how to repair it out, routed to the coding agent or the technician — and verified on the next recording.
Author: TrialONE contributors
License: TrialONE — Proprietary Software License
        Copyright (c) 2026 TrialONE / SuperNova.space. All rights reserved.
        
        This software, including its source code, object code, and documentation (the
        "Software"), is the confidential and proprietary property of TrialONE /
        SuperNova.space (the "Licensor"). The Software is licensed, not sold. No title
        or ownership is transferred.
        
        1. GRANT. Subject to your compliance with this License, the Licensor grants you
           a personal, non-exclusive, non-transferable, non-sublicensable, revocable
           license to install and run the Software to evaluate it and to test robots
           that you own or are authorized to test.
        
        2. RESTRICTIONS. You may NOT, in whole or in part, directly or indirectly:
           (a) copy, publish, distribute, sublicense, sell, rent, lease, or otherwise
               make the Software available to any third party;
           (b) modify, adapt, translate, or create derivative works of the Software;
           (c) reverse engineer, decompile, disassemble, or otherwise attempt to derive
               the source code, algorithms, or internal design of the Software, except
               and only to the extent this restriction is prohibited by applicable law;
           (d) remove, obscure, or alter any proprietary or copyright notices;
           (e) use the Software, or any idea or know-how it embodies, to design, build,
               train, or improve a product or service that competes with the Software.
        
        3. FEEDBACK. If you send the Licensor feedback or suggestions, the Licensor may
           use them freely, without restriction or obligation to you.
        
        4. RESERVATION. All rights not expressly granted are reserved by the Licensor.
        
        5. NO WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" AND "AS AVAILABLE", WITHOUT
           WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
           WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
           NONINFRINGEMENT. The Software performs adversarial fault testing of robots;
           you are solely responsible for running it only in safe, authorized, simulated
           or otherwise controlled environments.
        
        6. LIMITATION OF LIABILITY. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT
           SHALL THE LICENSOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
           CONSEQUENTIAL, OR EXEMPLARY DAMAGES, OR FOR ANY DAMAGE TO PERSONS, ROBOTS, OR
           PROPERTY, ARISING OUT OF OR RELATED TO THE SOFTWARE OR ITS USE.
        
        7. TERMINATION. This License terminates automatically and immediately if you
           breach any term. On termination you must stop all use and destroy all copies.
        
        8. EVALUATION / BETA. The Software may be provided as a free, capped, or beta
           release for evaluation. The Licensor may change, limit, suspend, or withdraw
           access, features, or quotas at any time.
        
        For commercial licensing, redistribution, or any permission beyond this License,
        contact the Licensor (malhiharwinsingh@gmail.com).
        
Project-URL: Homepage, https://trialone.dev
Project-URL: Documentation, https://trialone.wiki
Keywords: ros2,robotics,diagnostics,fault-diagnosis,rosbag,mcap,telemetry,predictive-maintenance
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development :: Debuggers
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi
Requires-Dist: uvicorn[standard]
Requires-Dist: pyyaml
Requires-Dist: click
Requires-Dist: mcap
Requires-Dist: rosbags
Requires-Dist: truststore
Requires-Dist: numpy
Requires-Dist: h5py
Requires-Dist: pandas
Requires-Dist: pyulog
Requires-Dist: pymavlink
Requires-Dist: prompt_toolkit
Provides-Extra: dev
Requires-Dist: ruff; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pyyaml; extra == "dev"
Provides-Extra: llm
Requires-Dist: anthropic>=0.69; extra == "llm"
Provides-Extra: proxy
Requires-Dist: fastapi; extra == "proxy"
Requires-Dist: uvicorn[standard]; extra == "proxy"
Requires-Dist: anthropic>=0.69; extra == "proxy"
Dynamic: license-file

<div align="center">

# TrialONE

### Fault diagnosis for real robots

**Connect your robot — TrialONE finds what no self-test knows, explains it, and hands the repair to
whoever can carry it out.**

Software faults go to the customer's coding agent as a patch target.
Physical faults go to a technician, with the measurement behind them.

</div>

---

## What it does

A robot fails in the field. Reading the recording costs a day of someone's time and usually ends in
a guess — because the interesting faults are the ones the machine's own monitors never named.

TrialONE answers three questions:

1. **Where is the fault** — which channel, which joint, which topic, which node, at which instant.
2. **Is it software, hardware or configuration** — and therefore who can repair it.
3. **What exactly to change** — reached by ruling mechanisms out against measured numbers, not by
   listing everything it could be.

It works on a recording today and is built to read the machine directly (see
[the loop](#the-loop-connect--diagnose--repair--prove), below).

```bash
trialone diagnose crash.log --project ~/robot_ws --description robot.urdf --json
```

`--project` and `--description` are optional. They are what turn *"check the topic your costmap
reads from"* into a file and a line, and *"82x its own normal band"* into *"53% of what this joint
is rated for"*. You point at the tree; the files are recognised by their structure, not by their
names, so nobody has to pick them out.

## One entry point, several lanes

`diagnose(path)` sniffs the input and routes it. There is no format the customer has to produce.

| Input today | What is measured |
|---|---|
| ROS 2 console / launch log | decoded middleware, TF and Nav2 invariants, ordered by causal depth |
| ROS 1 bag, ROS 2 MCAP, HDF5, CSV | numeric signals — detection, then per-incident analysis |
| Firmware panic (ARM Cortex-M / NuttX), GDB backtrace | fault registers and the faulting frame |
| PX4 ULog, ArduPilot BIN / tlog | the autopilot's own coded fault records, plus telemetry |

## The agent layer is the product

A log parser reformats what the robot already reported. That is a commodity, and it is not this.

The engine finds what **no self-test knows** — and then it has to say what that thing *is*, on a
machine whose signals are named by its own engineers. `mtr_amp_j4`, `tau_ext_hat_filtered[0]`,
`Force_x`: no table of names survives contact with the second customer. So the work is split, and
the split is the point:

| Deterministic mathematics | Agent layer |
| --- | --- |
| **Detection** — which operation is anomalous, and by how much | **Semantics** — what this signal *is*, and what high or low means for it |
| reproducible, auditable, no model in the loop | open vocabulary — any naming any customer invents |
| decides **whether** | explains **what** and **why**, as a causal chain |

The agent never decides whether something is broken; a model that is asked "fault or manoeuvre?"
answers unstably, and we measured that. Asked "what is `mtr_amp_j4`?" it is reliable. Detection stays
deterministic so a finding can be re-derived years later; meaning stays open so the engine works on a
robot nobody has seen.

Flags and state channels are held out of the ranking on purpose. A collision flag has no baseline
spread, so it wins any ranking trivially — and reporting it back is telling the operator what they
already knew.

## Elimination, not a list of possibilities

*"Check alignment, wear, or an obstruction"* is a list of everything it could be, which is the
absence of a diagnosis. Every candidate mechanism carries **falsifiable predictions**; the engine
runs them and records the number that killed each one:

```
RULED OUT by this recording:
  - a one-off external load — ruled out: 7 repetitions spaced 4.69 s apart, jitter 0.7%.
  - the axis is binding — ruled out: the excursion lasted 350 ms and then ended.
  - a part is degrading — ruled out: the peak varies by 0.4% across 7 repetitions, no trend.
  - the estimate is wrong — ruled out: an independently measured channel agreed, at 63x its band.
```

What survives is stated with its evidence. When two survive, the report names the **single
measurement that separates them** rather than picking the likelier one.

## Measured, on real data

The strongest detector compares against healthy reference operations, which is why the target is
machines that **repeat** — AMRs, manipulators, quadrupeds, humanoids.

| Dataset | Result |
|---|---|
| UR5e cobot (AURSAD), through `diagnose(folder)` | 94% recall at 1.1% false alarms |
| AURSAD anomaly scoring | AUC 0.953 |
| Franka Panda contact recording, handed over as a plain CSV with no labels | all 7 contacts found, each within 0.02–0.08 s, every one localised to the joint the robot's own detector names |

Drone lanes exist and run, but are **frozen, not extended**: a flight repeats nothing, so label-free
detection there is unsolved (ALFA AUC 0.72). That is a measurement, not a positioning choice.

## The loop: connect → diagnose → repair → prove

A diagnosis that nobody can act on, or that nobody checks afterwards, is a report. The product is
the loop that closes:

1. **Connect.** Static first — the URDF, the parameter files, the launch configuration. That says
   what *could* break. Then dynamic: a recording, or the machine's own live stream. That says what
   actually moves.
2. **Diagnose.** Deterministic detection, agent-resolved meaning, mechanisms eliminated against
   measured numbers.
3. **Repair.** A recipe in fixed fields — symptom, causal chain, measured condition, change class,
   file and line, acceptance criterion — executable by a junior engineer or by a coding agent, and
   rendered by a web view, without either of them parsing prose.
4. **Prove.** The acceptance criterion decides when the case closes, and it refuses a partial
   improvement: a warning that merely gets rarer is the same fault with a quieter symptom, and a
   reduced peak is not a pass. The mechanism is removed, or it is not.

**Where this stands.** Steps 1–3 run today on recordings, with the static side reading URDF and
parameter files. Step 4 exists as the stated criterion, not yet as an executed re-test. Reading the
machine live — watching a signal drift toward a fault before it becomes one — is the direction this
is built for and is **not implemented yet**; it is the same engine pointed at a stream instead of a
file, which is why detection was built to run on the recording's own baseline rather than on a
library of known faults.

## What it cannot do yet

Stated plainly, because a tool that hides its limits cannot be trusted with the ones it does not:

- A topic string in a log has already been through namespace expansion and remapping, so it usually
  appears in no source file. Resolving a QoS fault to the declaration that sets it needs the launch
  configuration, which is not built yet.
- The blast radius of a change — how many nodes depend on an interface — needs the node/topic graph.
- The repeated-operations lane still emits the generic per-domain sentence, visibly labelled as one.
- Locating an incident to a specific waypoint needs forward kinematics from the URDF, not yet wired.
- Live readout from a running machine, and the re-test that closes the loop, are not implemented.

The first four appear in the output itself as a **stated gap naming the input that would close it**,
never as a missing field.

## Trust rules

- Never claim a green we have not earned. No invented benchmarks, no fabricated logos.
- Unreadable input (encrypted, truncated) produces an honest stop, never a manufactured diagnosis.
- A file and line is marked **proven** only when a number the log printed agrees with the file.
  Anything less is a **candidate** that names the gate it failed and the one artefact that would
  close it — a wrong file and line sends a coding agent to edit the wrong declaration, and looks
  authoritative doing it.
- No hardcoding of semantics. Decoding a documented protocol (ARM CFSR bits, the `ros__parameters`
  layout, an rclcpp message format) is a codec. Guessing what a signal means from its name is not —
  that is what the agent layer is for.

## Running it

```bash
pip install -e .
```

```bash
trialone diagnose <log>
```

```bash
trialone diagnose <log> --json
```

The JSON is one recipe per finding in fixed fields — symptom, causal chain, measured condition,
change class, sites, acceptance criterion — which is what a coding agent executes and what a web
view renders, without either of them parsing prose. Every finding carries a stable `id` built from
what identifies the fault rather than the run, which is what makes the next command possible.

### Did the repair work?

```bash
trialone verify findings.json <the recording made after the repair>
```

Re-measures the new recording against the previous diagnosis and rules per finding: **GONE**, **STILL**
or **NEW**. It runs without a model, so the verdict never depends on an API quota, and it exits 0 on pass
and 1 on fail so it drops into CI. All-old-findings-gone with new ones present is still a fail — a repair
that moves a fault is not a repair.

This is the half that makes the tool an acceptance rather than a report: you diagnose, your coding agent
or technician repairs, the machine runs again, and TrialONE says whether it held.

### Try it with no data of your own

```bash
trialone demo
```

Runs a full diagnosis on a bundled recording. No configuration, no key, no robot.

### Watch a running robot

```bash
trialone watch                      # a live ROS 2 graph
trialone watch --replay <recording> # the same path, fed from a file
```

Silent unless something changes. ROS 2 is needed only for the live graph; every other command works
without it.

### The model layer (optional)

Detection is deterministic and runs without any model. Interpretation — what a signal means, the causal
chain, the mechanism — uses one, and it is off until you configure it:

```bash
echo "ANTHROPIC_API_KEY=..." >> .env       # or GEMINI_API_KEY
echo "TRIALONE_LLM_PROVIDER=anthropic" >> .env
```

`.env` is gitignored. Without a key the engine still runs and says plainly that its signal
interpretation is the generic fallback. With one, `trialone diagnose <log> --explain` asks for a physical
mechanism and reports it **only with the checks the engine ran against it** — a story whose predictions
fail comes back refuted, with the failures named. Each run prints what it cost.

By default only signal names and units reach the model. With `--explain` its tools may also read measured
values — binned profiles, ranges, per-part comparisons — because a mechanism cannot be checked against
numbers nobody may look at. Raw recordings are never uploaded.

Tests and the structure guardrail:

```bash
PYTHONPATH=src python -m pytest tests/test_engine.py -q
```

```bash
PYTHONPATH=src python tools/structure_check.py
```

## What lives where

| Path | What it is |
| --- | --- |
| `src/trialone/` | the engine and the CLI |
| `web/` | the website and the web app (Next.js, deployed to trialone.dev) |
| `deploy/` | the robot container and the model proxy |
| `ui/` | **frozen** — see below |

`web/` moved in from its own repository on 2026-08-27. The two halves share one object: the
engine writes the nine-field recipe (`diagnose/recipe.py::to_payload`) and the site renders it
without computing anything of its own. They are in one repository so that one test can hold them
to the same field list; `docs/project/TERMINAL-ZU-WEB.md` is the contract between them.

**`ui/` — the Electron desktop app — is frozen.** Not deleted, not developed further. The reason
is a count rather than a preference: `ui/src/engine.ts` calls **63 distinct `/api/…` paths** and
`src/trialone/server.py` serves **4 routes**, so the app talks to a product this repository no
longer is. What replaces it is `trialone server start` for the offline view on the robot itself,
and `web/` for everything that needs an account. `ui/supabase/` stays valid — it is the same
sign-in the website uses.

## Status

Pre-launch, in active development. Contact **malhiharwinsingh@gmail.com**.
