<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Harshul Dwivedi</title>
    <description>The latest articles on DEV Community by Harshul Dwivedi (@harshuldwivedi).</description>
    <link>https://dev.to/harshuldwivedi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2394159%2F39291196-6e52-445b-a095-8e5951b8e3ef.png</url>
      <title>DEV Community: Harshul Dwivedi</title>
      <link>https://dev.to/harshuldwivedi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/harshuldwivedi"/>
    <language>en</language>
    <item>
      <title>Building an AI Forensic Investigator for Vehicle Failures</title>
      <dc:creator>Harshul Dwivedi</dc:creator>
      <pubDate>Sat, 29 Aug 2026 10:25:43 +0000</pubDate>
      <link>https://dev.to/harshuldwivedi/building-an-ai-forensic-investigator-for-vehicle-failures-cjp</link>
      <guid>https://dev.to/harshuldwivedi/building-an-ai-forensic-investigator-for-vehicle-failures-cjp</guid>
      <description>&lt;h1&gt;
  
  
  I built an AI agent that diagnoses cars — and asks before it touches anything
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Built for the TrueForge Agent Harness Hackathon (Aug 24–30, 2026).&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Something expensive broke. Figure out why and prove it. Ask a human before you touch anything."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was basically the idea behind FaultTrace.&lt;/p&gt;

&lt;p&gt;I wanted to build something that felt more like an actual investigation than another chatbot with a few tools attached. The result is an autonomous vehicle-forensics agent that can gather evidence, test competing explanations, run actual analysis, and stop when it reaches a physical-world action that needs a human.&lt;/p&gt;

&lt;p&gt;And honestly, the interesting part wasn't getting the first version working.&lt;/p&gt;

&lt;p&gt;It was getting the whole thing to &lt;strong&gt;keep working reliably&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is how I built it, and what went wrong along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why diagnose a car with an agent?
&lt;/h2&gt;

&lt;p&gt;A car throws:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;P0171 — System Too Lean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A chatbot can explain what P0171 means in a few seconds.&lt;/p&gt;

&lt;p&gt;But that's not really the hard part.&lt;/p&gt;

&lt;p&gt;A technician still has to figure out &lt;em&gt;why&lt;/em&gt; the car thinks it's running lean. It could be a vacuum leak, a dirty MAF sensor, a fuel-delivery problem, or even an O2 sensor that's giving misleading information.&lt;/p&gt;

&lt;p&gt;So the real problem isn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What does P0171 mean?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Which of several possible causes actually explains the evidence?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's an investigation.&lt;/p&gt;

&lt;p&gt;That distinction is what led me to build &lt;strong&gt;FaultTrace&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Given a vehicle failure event, FaultTrace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gathers evidence from multiple sources,&lt;/li&gt;
&lt;li&gt;creates competing root-cause hypotheses,&lt;/li&gt;
&lt;li&gt;predicts what each hypothesis should look like in the data,&lt;/li&gt;
&lt;li&gt;runs actual analysis in a sandbox,&lt;/li&gt;
&lt;li&gt;uses deterministic Bayesian calculations to rank the hypotheses,&lt;/li&gt;
&lt;li&gt;calculates which additional test would reduce uncertainty the most,&lt;/li&gt;
&lt;li&gt;and stops for human approval before taking a physical-world action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the hackathon, I kept the scope deliberately concrete: &lt;strong&gt;vehicle diagnostic forensics&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The hero scenario is a cracked brake-booster vacuum hose on a 2003 Honda Accord, resulting in &lt;code&gt;P0171&lt;/code&gt; + &lt;code&gt;P0300&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's a small enough problem to demonstrate end-to-end, but complicated enough to make the agent actually investigate rather than just look up a DTC.&lt;/p&gt;




&lt;h1&gt;
  
  
  The architecture
&lt;/h1&gt;

&lt;p&gt;The vehicle domain is the implemented MVP. The underlying investigation pattern is intended to generalize to other safety-sensitive physical systems later.&lt;/p&gt;

&lt;p&gt;Here's the high-level architecture:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TB

    U["User / Technician"] --&amp;gt; AG

    subgraph TF["TrueForge Harness"]

        direction TB

        AG["Investigator Agent&amp;lt;br/&amp;gt;(faulttrace-investigator)"]

        SUB["Dynamic Subagents&amp;lt;br/&amp;gt;(per-hypothesis fan-out)"]

        SBX["Harness sandbox&amp;lt;br/&amp;gt;(optional agent-generated checks)"]

        RANK["Bayesian ranking&amp;lt;br/&amp;gt;prior × likelihood → posterior"]

        SES["Persistent session"]

    end

    subgraph MCP["faulttrace-vehicle MCP server"]

        R1["get_dtcs · get_freeze_frame"]

        R2["get_sensor_log · get_compact_telemetry"]

        R3["lookup_dtc_knowledge · get_vehicle_info"]

        RA["run_analysis"]

        G2["request_measurement — Tier 2"]

        G3["clear_codes · order_part — Tier 3"]

    end

    AG --&amp;gt; R1
    AG --&amp;gt; R2
    AG --&amp;gt; R3
    AG --&amp;gt; SES

    AG -- "hypothesis fan-out" --&amp;gt; SUB
    SUB -- "supporting / contradictory evidence" --&amp;gt; AG

    AG -- "run analysis" --&amp;gt; RA
    RA --&amp;gt; FIXED["fixed analyze.py (server-side, deterministic)"]
    FIXED --&amp;gt; RANK
    RANK --&amp;gt; AG

    SUB -- "optional custom checks" --&amp;gt; SBX

    AG -- "propose physical action" --&amp;gt; AP["Human approval gate"]
    AP -- "approved → invoke" --&amp;gt; G2
    AP -- "approved → invoke" --&amp;gt; G3
    AP -- "rejected → cancel" --&amp;gt; XL["no tool call"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The important thing here isn't the number of boxes.&lt;/p&gt;

&lt;p&gt;It's the loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The investigation loop
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failure event
(DTC + freeze-frame + sensor conditions)
        ↓
Observe
(read-only evidence via MCP)
        ↓
Form competing root-cause hypotheses
(each with a predicted signature)
        ↓
Run computed analysis
(real computation, not LLM text math)
        ↓
Evaluate supporting vs contradictory evidence
        ↓
Bayesian update
prior × likelihood → posterior differential
        ↓
Identify remaining uncertainty
        ↓
Choose the next diagnostic
(expected information gain + cost)
        ↓
STOP for human approval
before Tier 2 / Tier 3 actions
        ↓
New evidence arrives
        ↓
Continue investigation
        ↓
Defensible root-cause conclusion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the part I wanted to get right.&lt;/p&gt;

&lt;p&gt;The model isn't supposed to just produce a diagnosis and call it a day. It needs to figure out what it knows, what it doesn't know, and what it should do next.&lt;/p&gt;




&lt;h1&gt;
  
  
  What makes this different from a chatbot?
&lt;/h1&gt;

&lt;p&gt;This was one of the design questions I kept coming back to.&lt;/p&gt;

&lt;p&gt;If I could replace the whole system with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Paste your DTC into ChatGPT"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;then I hadn't really built an agent.&lt;/p&gt;

&lt;p&gt;FaultTrace needs to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retrieve evidence from multiple tools,&lt;/li&gt;
&lt;li&gt;delegate specialized investigation,&lt;/li&gt;
&lt;li&gt;form competing hypotheses,&lt;/li&gt;
&lt;li&gt;determine what each hypothesis predicts,&lt;/li&gt;
&lt;li&gt;execute computational analysis,&lt;/li&gt;
&lt;li&gt;compare evidence,&lt;/li&gt;
&lt;li&gt;identify missing evidence,&lt;/li&gt;
&lt;li&gt;choose a useful next diagnostic,&lt;/li&gt;
&lt;li&gt;pause for human approval,&lt;/li&gt;
&lt;li&gt;receive the result,&lt;/li&gt;
&lt;li&gt;and continue the same investigation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the distinction is pretty simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A chatbot gives you an answer. FaultTrace runs an investigation.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  TrueForge isn't just there for the hackathon points
&lt;/h1&gt;

&lt;p&gt;This was important to me.&lt;/p&gt;

&lt;p&gt;I didn't want to build a normal chatbot and then bolt TrueForge onto it just so I could say I used the sponsor's technology.&lt;/p&gt;

&lt;p&gt;The harness is actually doing a lot of the work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent runtime
&lt;/h3&gt;

&lt;p&gt;FaultTrace runs as a TrueForge agent, defined by a manifest containing the model, instructions, and MCP configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  MCP
&lt;/h3&gt;

&lt;p&gt;The agent talks to a real vehicle MCP server over HTTP.&lt;/p&gt;

&lt;p&gt;The tools aren't simulated function descriptions sitting inside the prompt. The agent actually reaches the server and gets data back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic subagents
&lt;/h3&gt;

&lt;p&gt;FaultTrace can fan out the investigation by hypothesis.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    FaultTrace
                        │
              Bayesian differential
                        │
          ┌─────────────┼─────────────┐
          ↓             ↓             ↓
     Vacuum leak     Misfire /      Sensor
     investigator    ignition     plausibility
                     investigator   investigator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each subagent gets its own thread and sandbox and reports back supporting and contradictory evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sandbox
&lt;/h3&gt;

&lt;p&gt;The model can propose analysis, but the important computation happens in code.&lt;/p&gt;

&lt;p&gt;The fixed &lt;code&gt;analyze.py&lt;/code&gt; library performs the deterministic diagnostic calculations, including the Bayesian differential and expected information gain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Persistent sessions
&lt;/h3&gt;

&lt;p&gt;An investigation isn't just one request/response.&lt;/p&gt;

&lt;p&gt;The TrueForge session can pause for an approval, reconnect, and continue the same investigation. In the demo, the investigation actually resumes mid-flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Human approval
&lt;/h3&gt;

&lt;p&gt;This is probably my favorite part.&lt;/p&gt;

&lt;p&gt;If the agent decides it needs a physical measurement or wants to clear codes/order a part, the harness pauses the action and puts the decision in front of a human.&lt;/p&gt;

&lt;p&gt;Nothing executes until it's approved.&lt;/p&gt;




&lt;h1&gt;
  
  
  The MCP tools
&lt;/h1&gt;

&lt;p&gt;The vehicle MCP server currently exposes 13 tools across the investigation and safety workflow.&lt;/p&gt;

&lt;p&gt;Some of the important ones are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_vehicles&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Discover available vehicles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_vehicle_info&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retrieve vehicle metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_dtcs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retrieve diagnostic trouble codes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_freeze_frame&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retrieve the fault-state snapshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_pid_list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Discover available telemetry PIDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_compact_telemetry&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retrieve bounded current telemetry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_sensor_log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retrieve historical sensor telemetry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lookup_dtc_knowledge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retrieve scenario-specific diagnostic knowledge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;run_analysis&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run the deterministic computed differential&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;request_measurement&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request an additional diagnostic measurement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;clear_codes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clear diagnostic codes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;order_part&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request a replacement part&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last two are intentionally gated.&lt;/p&gt;

&lt;p&gt;That distinction matters because the agent can be autonomous without being allowed to do whatever it wants.&lt;/p&gt;




&lt;h1&gt;
  
  
  Letting the data actually decide
&lt;/h1&gt;

&lt;p&gt;One thing I didn't want was a model looking at a bunch of numbers and then casually saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'm 91% confident this is a vacuum leak."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's not very convincing.&lt;/p&gt;

&lt;p&gt;So FaultTrace has a deterministic analysis layer.&lt;/p&gt;

&lt;p&gt;The model supplies the investigation context, but the actual analysis code calculates the differential.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;P(hypothesis | evidence)
        ∝
P(evidence | hypothesis) × P(hypothesis)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The analyzer takes scenario-specific priors and telemetry-derived likelihoods, performs the Bayesian update, and returns a normalized posterior ranking.&lt;/p&gt;

&lt;p&gt;It also calculates &lt;strong&gt;expected information gain&lt;/strong&gt; for the available diagnostic tests.&lt;/p&gt;

&lt;p&gt;That gives the agent something more useful than "try another test":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current uncertainty
        ↓
Evaluate available tests
        ↓
Calculate expected information gain
        ↓
Consider test cost
        ↓
Select the most useful next test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is reproducible because the computation is deterministic and seeded.&lt;/p&gt;

&lt;p&gt;That separation is important to the architecture:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The LLM decides what to investigate. The analysis code calculates the numbers.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  The predicted-signature idea
&lt;/h1&gt;

&lt;p&gt;Every hypothesis isn't just a label.&lt;/p&gt;

&lt;p&gt;It comes with a prediction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If this hypothesis is actually true, what should I see in the data?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, a vacuum leak should produce a different pattern from a MAF fault or an ignition problem.&lt;/p&gt;

&lt;p&gt;That gives the agent something concrete to test.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hypothesis: Vacuum leak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hypothesis:
Vacuum leak

Predicted signature:
- elevated fuel trims
- stronger effect at idle
- abnormal airflow relationship
- correlation with misfire behavior
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The analysis then checks the telemetry against those signatures.&lt;/p&gt;

&lt;p&gt;This also makes the final explanation much more useful because we can show both:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;why a hypothesis fits&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;why another hypothesis doesn't.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Supporting evidence isn't enough
&lt;/h1&gt;

&lt;p&gt;I wanted the agent to actively look for evidence &lt;em&gt;against&lt;/em&gt; its own hypotheses too.&lt;/p&gt;

&lt;p&gt;So a final differential isn't just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vacuum leak
✓ Fuel trims support this
✓ MAF relationship supports this
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should look more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VACUUM LEAK

Supporting evidence
✓ Positive fuel trim at idle
✓ Airflow relationship matches predicted behavior
✓ Misfire pattern is consistent

Contradictory evidence
⚠ Idle instability is weaker than expected

Missing evidence
? Fuel pressure under load
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for another hypothesis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MAF FAULT

Supporting evidence
✓ Some airflow irregularity

Contradictory evidence
✕ Fuel-trim behavior is more consistent with
  unmetered air

Missing evidence
? Independent airflow measurement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That "why not?" reasoning is a big part of making the result feel forensic rather than classificatory.&lt;/p&gt;




&lt;h1&gt;
  
  
  Active diagnosis: knowing what to test next
&lt;/h1&gt;

&lt;p&gt;This is another part I really wanted to avoid making into a hard-coded flowchart.&lt;/p&gt;

&lt;p&gt;Suppose the agent has narrowed the problem down to two plausible causes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Vacuum leak
2. Weak fuel delivery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent shouldn't just say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"More data is needed."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What measurement would actually separate these two explanations?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's where expected information gain comes in.&lt;/p&gt;

&lt;p&gt;The analysis evaluates the available tests and returns something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Recommended test:
fuel_pressure_under_load

Expected information gain:
X.XX bits

Cost:
Low

Reason:
The result is expected to distinguish the two leading hypotheses.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then the agent stops.&lt;/p&gt;

&lt;p&gt;It doesn't execute the physical test automatically.&lt;/p&gt;




&lt;h1&gt;
  
  
  The three-tier safety model
&lt;/h1&gt;

&lt;p&gt;This is deliberately simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tier 1 — Investigate
&lt;/h3&gt;

&lt;p&gt;The agent can do these autonomously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read DTCs,&lt;/li&gt;
&lt;li&gt;retrieve telemetry,&lt;/li&gt;
&lt;li&gt;inspect history,&lt;/li&gt;
&lt;li&gt;look up diagnostic knowledge,&lt;/li&gt;
&lt;li&gt;generate hypotheses,&lt;/li&gt;
&lt;li&gt;run analysis,&lt;/li&gt;
&lt;li&gt;use the sandbox.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tier 2 — Diagnose physically
&lt;/h3&gt;

&lt;p&gt;Human approval required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;request a measurement,&lt;/li&gt;
&lt;li&gt;run a guided diagnostic procedure,&lt;/li&gt;
&lt;li&gt;collect data that requires an active diagnostic action.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tier 3 — Change the vehicle / external world
&lt;/h3&gt;

&lt;p&gt;Human approval required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clear codes,&lt;/li&gt;
&lt;li&gt;order a part,&lt;/li&gt;
&lt;li&gt;modify vehicle state,&lt;/li&gt;
&lt;li&gt;perform a physical repair.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Investigate freely. Act carefully.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are two layers of protection here. TrueForge provides the actual approval gate, and the MCP server independently refuses gated calls that don't contain the required approval.&lt;/p&gt;

&lt;p&gt;So even if something goes wrong in the agent layer, the server has another line of defense.&lt;/p&gt;




&lt;h1&gt;
  
  
  The part that almost killed the project: models are not interchangeable
&lt;/h1&gt;

&lt;p&gt;This was probably the biggest practical lesson I got from the build.&lt;/p&gt;

&lt;p&gt;I initially assumed that if a model was good enough at reasoning, I could just swap it into the same agent and everything would behave roughly the same.&lt;/p&gt;

&lt;p&gt;Nope.&lt;/p&gt;

&lt;p&gt;The behavior around tools and subagents can be dramatically different.&lt;/p&gt;

&lt;h2&gt;
  
  
  First problem: GLM
&lt;/h2&gt;

&lt;p&gt;I initially used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openrouter/z-ai-glm-5.3-flash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because it was cheap and fast.&lt;/p&gt;

&lt;p&gt;It did something interesting: it actually spawned real &lt;code&gt;create_sub_agent&lt;/code&gt; threads.&lt;/p&gt;

&lt;p&gt;I could see three separate &lt;code&gt;thread.created&lt;/code&gt; events with different thread IDs.&lt;/p&gt;

&lt;p&gt;So, great?&lt;/p&gt;

&lt;p&gt;Not quite.&lt;/p&gt;

&lt;p&gt;The subagents then hit a wall because the harness's local sandbox is macOS/Linux only, while I was running the project on Windows. They ended up trying to execute their Python in a cloud sandbox and timing out.&lt;/p&gt;

&lt;p&gt;So I had:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;beautiful fan-out
       ↓
three real subagents
       ↓
sandbox timeout
       ↓
zero useful results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That wasn't exactly the demo I wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then Gemini
&lt;/h2&gt;

&lt;p&gt;I switched the primary model to Gemini 2.5 Flash.&lt;/p&gt;

&lt;p&gt;Now I had the opposite problem.&lt;/p&gt;

&lt;p&gt;Gemini would talk about subagents without actually creating them.&lt;/p&gt;

&lt;p&gt;I'd see things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sub-agent: investigating vacuum leak...
Sub-agent: checking ignition...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but there were no real &lt;code&gt;thread.created&lt;/code&gt; events.&lt;/p&gt;

&lt;p&gt;It was essentially role-playing the delegation.&lt;/p&gt;

&lt;p&gt;That's a surprisingly important distinction when you're building an agent system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tightening the instructions
&lt;/h2&gt;

&lt;p&gt;I eventually had to make delegation explicit and give the subagents very concrete instructions.&lt;/p&gt;

&lt;p&gt;Things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use the VIN from the actual failure event,&lt;/li&gt;
&lt;li&gt;use the exact PID names,&lt;/li&gt;
&lt;li&gt;don't invent telemetry,&lt;/li&gt;
&lt;li&gt;write Python to a file before running it,&lt;/li&gt;
&lt;li&gt;use the sandbox for actual computation,&lt;/li&gt;
&lt;li&gt;return supporting and contradictory evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One small example caused a ridiculous amount of pain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;was the actual PID.&lt;/p&gt;

&lt;p&gt;The subagent would sometimes assume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;engine_rpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the whole analysis would fail.&lt;/p&gt;

&lt;p&gt;Another lesson: when running Python through the shell, I had much better results writing it to a file and executing the file than trying to pipe complex Python through &lt;code&gt;echo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Sometimes the "AI problem" is just shell quoting.&lt;/p&gt;




&lt;h1&gt;
  
  
  Another Gemini problem: "Should I proceed?"
&lt;/h1&gt;

&lt;p&gt;The other thing that bit me was the approval flow.&lt;/p&gt;

&lt;p&gt;I needed the model to produce the actual gated tool call so TrueForge could surface the approval UI.&lt;/p&gt;

&lt;p&gt;Instead, Gemini would sometimes do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The next step would be to request
a fuel-pressure measurement.

Would you like me to proceed?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks reasonable to a human.&lt;/p&gt;

&lt;p&gt;But it completely bypasses the actual approval mechanism.&lt;/p&gt;

&lt;p&gt;There was no approval button because there was no tool call.&lt;/p&gt;

&lt;p&gt;So I added an explicit rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If the agent has decided on a gated action, the turn must end with the gated tool call rather than a prose question.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was one of those tiny prompt changes that made a huge difference.&lt;/p&gt;

&lt;h1&gt;
  
  
  Qodo caught some things I would have missed
&lt;/h1&gt;

&lt;p&gt;I also ran Qodo code review on the project's pull requests.&lt;/p&gt;

&lt;p&gt;It wasn't just a checkbox for the hackathon. A few findings actually changed the implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Single source of truth
&lt;/h3&gt;

&lt;p&gt;Qodo flagged that &lt;code&gt;run_analysis&lt;/code&gt; could be treated as one source of posterior probabilities while the orchestrator had already derived another view from subagent evidence.&lt;/p&gt;

&lt;p&gt;That was a dangerous design.&lt;/p&gt;

&lt;p&gt;I changed the architecture so:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;run_analysis&lt;/code&gt; is the single authoritative source of posterior probabilities.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Subagents contribute evidence and narrative analysis, but they don't modify the posterior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ground truth leaking through the tool schema
&lt;/h3&gt;

&lt;p&gt;Qodo also caught a mismatch where a tool description exposed scenario information that the response itself intentionally kept hidden.&lt;/p&gt;

&lt;p&gt;I aligned the MCP contract with the actual allow-listed response.&lt;/p&gt;

&lt;h3&gt;
  
  
  The VIN bug
&lt;/h3&gt;

&lt;p&gt;This one was particularly important.&lt;/p&gt;

&lt;p&gt;A subagent recipe had accidentally hard-coded Scenario A's VIN.&lt;/p&gt;

&lt;p&gt;That meant Scenario B or C could have caused the subagent to investigate the wrong vehicle.&lt;/p&gt;

&lt;p&gt;Qodo caught it.&lt;/p&gt;

&lt;p&gt;The fix was simple: every subagent now receives and reuses the VIN from the actual failure event.&lt;/p&gt;

&lt;h3&gt;
  
  
  And sometimes the reviewer is wrong
&lt;/h3&gt;

&lt;p&gt;Qodo also suggested converting a smoke test to CommonJS.&lt;/p&gt;

&lt;p&gt;I pushed back on that one.&lt;/p&gt;

&lt;p&gt;The repository uses ESM with &lt;code&gt;"type": "module"&lt;/code&gt;, and the actual tests follow that convention. So I dismissed the suggestion with the reasoning recorded in the PR.&lt;/p&gt;

&lt;p&gt;That's a useful lesson too:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Code review tools are extremely useful, but they aren't infallible. You still need to understand the code you're reviewing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  The feature I'm most proud of isn't the Bayesian math
&lt;/h1&gt;

&lt;p&gt;It's the fact that FaultTrace &lt;strong&gt;knows when to stop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The agent can investigate a problem for as long as the work is read-only.&lt;/p&gt;

&lt;p&gt;But eventually it might conclude:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The next useful step is a physical measurement.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At that point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────┐
│         HUMAN APPROVAL REQUIRED             │
│                                             │
│  Request fuel-pressure measurement?         │
│                                             │
│       [ Approve ]       [ Reject ]          │
└─────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent waits.&lt;/p&gt;

&lt;p&gt;If the human rejects it, nothing happens.&lt;/p&gt;

&lt;p&gt;If the human approves it, the action executes and the same investigation can continue with the new evidence.&lt;/p&gt;

&lt;p&gt;That's the boundary I wanted:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Autonomous investigation. Human-controlled action.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  What's real and what's mocked?
&lt;/h1&gt;

&lt;p&gt;A hackathon project should be honest about this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;TrueForge agent orchestration&lt;/li&gt;
&lt;li&gt;MCP communication&lt;/li&gt;
&lt;li&gt;Dynamic subagent delegation&lt;/li&gt;
&lt;li&gt;Sandbox analysis&lt;/li&gt;
&lt;li&gt;Bayesian differential&lt;/li&gt;
&lt;li&gt;Expected information gain&lt;/li&gt;
&lt;li&gt;Persistent investigation sessions&lt;/li&gt;
&lt;li&gt;Human approval gates&lt;/li&gt;
&lt;li&gt;Vehicle diagnostic workflow&lt;/li&gt;
&lt;li&gt;Dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Simulated
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Vehicle telemetry is synthetic and seeded.&lt;/li&gt;
&lt;li&gt;There is no physical OBD-II connection.&lt;/li&gt;
&lt;li&gt;Parts ordering is mocked; no real money moves.&lt;/li&gt;
&lt;li&gt;The vehicle itself is simulated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scenarios B (dirty MAF) and C (stuck O2) exist for regression coverage.&lt;/p&gt;

&lt;p&gt;Scenario A (vacuum leak) is the hero.&lt;/p&gt;

&lt;p&gt;The broader architecture could eventually apply to industrial machinery, robotics, energy systems, and other physical systems, but those are &lt;strong&gt;future applications&lt;/strong&gt;, not claims about what this MVP already implements.&lt;/p&gt;




&lt;h1&gt;
  
  
  Running it
&lt;/h1&gt;

&lt;p&gt;The basic setup is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm start

&lt;span class="nb"&gt;cd &lt;/span&gt;mcp-server
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run start:http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The TrueForge harness runs on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8790
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository contains the remaining configuration and environment setup required to run the demo.&lt;/p&gt;




&lt;h1&gt;
  
  
  The demo
&lt;/h1&gt;

&lt;p&gt;I've kept the main demo focused on one investigation rather than trying to show every feature.&lt;/p&gt;

&lt;p&gt;The flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DTC event
   ↓
MCP evidence collection
   ↓
Competing hypotheses
   ↓
Dynamic subagents
   ↓
Sandbox analysis
   ↓
Bayesian differential
   ↓
Expected information gain
   ↓
Recommended diagnostic
   ↓
Human approval
   ↓
Investigation resumes
   ↓
Root-cause report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://drive.google.com/file/d/1fFy3QE3RxLscsi1MSokK8tBNNNGpMdye/view?usp=sharing" rel="noopener noreferrer"&gt;Watch the FaultTrace demo&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  What I learned
&lt;/h1&gt;

&lt;p&gt;If I had to summarize the whole project in one sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The hard part of an agent isn't getting a model to reason — it's making the entire loop reliable.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Getting an LLM to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I think this is a vacuum leak"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;is easy.&lt;/p&gt;

&lt;p&gt;Getting it to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;retrieve the correct vehicle,&lt;/li&gt;
&lt;li&gt;call the right MCP tools,&lt;/li&gt;
&lt;li&gt;form competing hypotheses,&lt;/li&gt;
&lt;li&gt;delegate real work,&lt;/li&gt;
&lt;li&gt;execute that work in a sandbox,&lt;/li&gt;
&lt;li&gt;use deterministic computations,&lt;/li&gt;
&lt;li&gt;choose the next useful test,&lt;/li&gt;
&lt;li&gt;stop at the physical-world boundary,&lt;/li&gt;
&lt;li&gt;get human approval,&lt;/li&gt;
&lt;li&gt;execute the action,&lt;/li&gt;
&lt;li&gt;resume the same session,&lt;/li&gt;
&lt;li&gt;and produce a defensible conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;is a very different problem.&lt;/p&gt;

&lt;p&gt;That's what made FaultTrace interesting to build.&lt;/p&gt;

&lt;p&gt;And that's probably the biggest thing I took away from the hackathon:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model is only one component of an agent. The orchestration around it is where the real engineering starts.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built for the TrueForge Agent Harness Hackathon. AI coding assistants were used during development, and the code was reviewed throughout the project, including with Qodo.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
