Houston, We Have a Problem: I am self-inspecting live and self-fixing if the risk is low.
· Billy Lo
If you run nightly batch jobs, ETL pipelines, or scrapers, you know the drill. Your script chugs along for two hours, gets 90% of the way through the queue, trips over one weird date string like "24/10/2026", and dies.
Over the last year, a lot of “AI for DevOps” tools have popped up to help with this. Almost all of them follow the exact same playbook:
- Your process crashes and exits.
- All the in-memory state disappears.
- The error gets shipped to a log file, Sentry, or Datadog.
- An AI agent reads the logs, notices the anomaly, guesses what went wrong, and suggests a code change (or opens a PR).
- You review the fix, merge it, and re-run the entire job from scratch.
That workflow is fine, and it beats paging someone at 3 a.m., but it misses a massive opportunity.
At the exact moment your code throws an exception, the Node.js process is still alive. Every local variable, every parsed payload, every open browser session or database connection is still sitting right there in V8 memory. Letting the process crash just so an agent can read a flat text log five minutes later is like breaking down on the side of the highway and calling a tow truck to haul your car all the way back to the shop, when the hood is already open and a loose battery cable just needed to be popped back on so you could finish the drive.
What if, instead of crashing, your script caught the error, opened Node’s built-in V8 debugger, and said:
“Houston, we have a problem… Investigate it live and fix it for me!”
We built a small, zero-dependency open-source library called Houston (houston-js) to do exactly that.
Dead-log forensics vs. live V8 introspection
Think about what happens when a parser fails on a weird record. In a log-based workflow, you only see whatever string you happened to put inside new Error(...). If you logged Invalid date, the agent reading your logs has no idea whether the field was "24/10/2026", null, a Unix timestamp in seconds, or an HTML error page that snuck into a JSON field. So you add more logging, redeploy, and wait for it to fail again.
When you pause the live process with Node’s native node:inspector module and connect an agent over the Chrome DevTools Protocol (CDP), the agent does not have to guess. It can inspect the actual object sitting in V8 memory, see that publishedAt is "24/10/2026", convert it to "2026-10-24" in place, and tell the loop to retry that single step.
Traditional "Log & Fix Later" Loop (Slow, loses state):
[Step Throws] -> [Process Dies] -> [Ship Logs] -> [AI Suggests PR] -> [Deploy] -> [Re-run Whole Job]
Houston "Live V8 Trap" Loop (Real-time, keeps state alive):
[Step Throws] -> [Pause & Open V8 Inspector] -> [AI Inspects Live Memory]
-> [Self-Assesses Risk] -> [Fixes Low-Risk Data in Place] -> [Retries Step & Finishes Job]
The job finishes its run on the first try. Nothing restarts.
Wait, you let an AI mutate live memory?! (Enter risk guardrails)
The obvious question with live self-healing is: “What stops the agent from ‘fixing’ a failing step by zeroing out an account balance or inventing a price?”
There is a big difference between fixing a formatting quirk and changing business data. You never want an agent guessing financial numbers just to make an error go away.
So we built risk self-assessment and pre-execution guardrails directly into Houston:
lowrisk: Lossless formatting fixes, like convertingDD/MM/YYYYto ISOYYYY-MM-DDor trimming stray whitespace.mediumrisk: Filling in missing optional metadata (like a default category tag) or updating a CSS selector in a scraper.highrisk: Touching anything involving money (price,royaltyFee,balance), inventory quantities, account IDs, or permissions.
By default, Houston runs with maxAllowedRisk: 'low'. Before the agent can touch V8 memory, it has to declare the risk level and justify why. During read-only inspection (inspect_runtime), currentItem is wrapped in a V8 Proxy that blocks accidental writes. Mutations have to go through mutate_runtime, which checks the risk level before sending anything over CDP.
If a step fails because a financial field is corrupt, the agent sees that fixing it is high risk, refuses to touch it, logs the incident as blocked, and lets your pipeline skip that single record safely.
Using Houston in 20 lines of code
Houston uses only built-in Node.js modules (node:inspector, node:child_process, node:fs, and native WebSocket / fetch in Node 22.4+). You can install it directly from GitHub:
npm install github:billylo1/Houston
Then wrap any flaky step in houston.trap():
import { Houston } from 'houston-js';
const houston = new Houston({
mode: 'auto', // Uses Gemini when GOOGLE_API_KEY is set, or built-in CDP healer offline
maxAllowedRisk: 'low', // Only auto-execute low-risk fixes
onRiskExceeded: 'skip', // Skip high-risk records instead of crashing the batch
auditLogPath: './houston_audit.jsonl',
});
const articles = [
{ id: 101, title: 'Launching Orbit v2', publishedAt: '2026-10-20', royaltyFee: 120.0 },
{ id: 102, title: 'Telemetry Deep Dive', publishedAt: '24/10/2026', royaltyFee: 85.5 }, // UK date format
{ id: 103, title: 'Zero-Downtime Workers', publishedAt: ' 2026-10-22 ', royaltyFee: 95.0 }, // Extra spaces
{ id: 104, title: 'Licensing Guide', publishedAt: '2026-10-23', royaltyFee: -450.0 }, // Corrupt negative fee!
];
for (const article of articles) {
const saved = await houston.trap(
`ingest-article-${article.id}`,
(item) => {
if (!/^\d{4}-\d{2}-\d{2}$/.test(item.publishedAt)) {
throw new Error(`Invalid ISO-8601 date for #${item.id}: "${item.publishedAt}"`);
}
if (typeof item.royaltyFee !== 'number' || item.royaltyFee < 0) {
throw new Error(`Invalid financial royaltyFee for #${item.id}: ${item.royaltyFee}`);
}
return { ...item, ingestedAt: Date.now() };
},
{ item: article },
);
if (saved) {
console.log(`Ingested #${saved.id}: ${saved.title}`);
}
}
houston.close();
When you run this (npm run example in the repo), here is what happens live:
- Article #101 sails right through.
- Article #102 throws on
"24/10/2026". Houston catches it, opensws://127.0.0.1:9229, and wakes up the agent. The agent inspectsglobalThis.__houston.currentItemover CDP, rates date normalization aslowrisk, rewritespublishedAtto"2026-10-24"in memory, and retries. Article #102 succeeds. - Article #103 throws on
" 2026-10-22 ". The agent trims the whitespace in memory (lowrisk) and retries. Article #103 succeeds. - Article #104 throws on
royaltyFee: -450. The agent inspects the object, recognizesroyaltyFeeas a financial number, rates any change to it ashighrisk, and blocks the fix so a human can look at it.
Morning coffee review: the audit log
You should never let an agent fix things silently in the dark without telling you. If an upstream API changed its date format, you want to know about it the next morning so you can update your parser permanently.
Every time Houston steps in, it appends a JSON record to houston_audit.jsonl with the before and after values, the assessed risk level, and the agent’s explanation:
{
"timestamp": "2026-10-24T14:20:15.440Z",
"stepId": "ingest-article-102",
"error": "Invalid ISO-8601 date for article #102 (\"Telemetry Deep Dive\"): \"24/10/2026\"",
"action": "retry",
"riskLevel": "low",
"maxAllowedRisk": "low",
"riskJustification": "Date string normalization from DD/MM/YYYY to ISO-8601 YYYY-MM-DD is a safe, non-destructive formatting fix.",
"originalValue": {
"id": 102,
"title": "Telemetry Deep Dive",
"publishedAt": "24/10/2026",
"royaltyFee": 85.5
},
"fixedValue": {
"id": 102,
"title": "Telemetry Deep Dive",
"publishedAt": "2026-10-24",
"royaltyFee": 85.5
},
"reason": "Normalized publishedAt date from DD/MM/YYYY ('24/10/2026') to ISO-8601 YYYY-MM-DD ('2026-10-24')."
}
And right below it, you see the high-risk item that Houston refused to touch:
{
"timestamp": "2026-10-24T14:20:36.471Z",
"stepId": "ingest-article-104",
"error": "Invalid financial royaltyFee for article #104 (\"Enterprise Licensing Guide\"): -450",
"action": "blocked",
"riskLevel": "high",
"maxAllowedRisk": "low",
"riskJustification": "Modifying financial amounts, prices, or royalty fees is classified as high risk under the risk policy.",
"originalValue": {
"id": 104,
"title": "Enterprise Licensing Guide",
"publishedAt": "2026-10-23",
"royaltyFee": -450
},
"fixedValue": null,
"reason": "Automatic healing blocked: article #104 contains a negative financial royaltyFee (-450). Requires manual human review."
}
You get the best of both worlds: your two-hour batch job doesn’t die over a silly date format at 2 a.m., sensitive fields stay protected, and you still get a clean diff the next morning showing what needs a permanent code fix.
Check out the code and example app at github.com/billylo1/Houston.