Showing Why Hindsight Recalled a Past Incident in the UI

An AI that says "this looks like last month's outage" is making a claim. The interface has to let an engineer check it.

I built the frontend for OpsRecall, an incident-response assistant that analyses a new production failure and, when it can, compares it with past incidents retrieved from Hindsight.

The hard part was not making it look good. It was deciding what the screen owes someone who is stressed, on call, and about to act on a suggestion.

What the Screens Do

The app is built with React, TypeScript, Vite, and Tailwind CSS, talking to an Express API.

The flow has five screens: a dashboard, an incident list, a submit form, an analysis page, and a confirmation page where an engineer records the real root cause and fix.

A separate Historical Memory page lists what the team has taught the system.

The design is deliberately calm: pastel colours, lots of white space, and one accent colour.

Incident tools get used at the worst moments, and a wall of red and black adds stress without adding information.

Severity gets a small coloured badge rather than a screaming banner.

The Through-Line: Provenance

Everything on the analysis page answers one question:

Where did this come from?

The Hindsight documentation describes recall as returning relevant facts for a query.

That is only useful in a UI if the person can see which facts, from which past incident, and what the model did with them.

So each recalled memory gets its own card.

The card is driven by a record my backend builds for every memory that was actually placed in the model's prompt.

This is the part of the component that renders the engineer-verified facts:

{m.verifiedRecord ? ( <> <div className="provenance-item"> <Icon name="check" size={15} /> <div> <span>Confirmed root cause</span> <p>{m.confirmedRootCause}</p> </div> </div> <div className="provenance-item"> <Icon name="check" size={15} /> <div> <span>Previous resolution</span> <p>{m.resolutionApplied}</p> </div> </div> <div className="provenance-item"> <Icon name="check" size={15} /> <div> <span>Verification then</span> <p> {m.verificationResults} · service recovered: {m.serviceRecovered ? 'yes' : 'no'} </p> </div> </div> </> ) : ( <p className="field-hint"> Recalled facts only — no engineer-verified record matched. </p> )} 

The distinction between "engineer-verified record" and "recalled facts only" is visible, not hidden.

If the backend could not find the full confirmed record, the card says so instead of dressing up a fragment as established history.

The same card shows the shared signals (same service, overlapping terms such as a pool name or error code), the differences the model noted, and a plain sentence for how the memory influenced the analysis.

A collapsible section shows the raw facts returned by Hindsight, so a skeptical engineer can read the source material.

Before and After, Side by Side

The page has two buttons:

  • Analyse with memory
  • Analyse without memory

Run both and the page shows them next to each other.

const canCompare = !!latestPlain && !!latestMem; 

The left card is labelled "Before: no memory".

The right card is "After: with Hindsight memory", and it shows how many past incidents were referenced.

I built the comparison because "memory makes it better" is an easy claim and a hard one to believe.

Seeing the two analyses together, with the number of referenced incidents next to each, lets the viewer judge for themselves, including in the cases where memory changes little.

Those cases exist on purpose.

One of the synthetic scenarios is an HTTP 502 from an API gateway with no database evidence.

A past database-pool incident is not relevant to it, and the interface is built so an irrelevant memory is labelled "Not relevant (not used)" rather than quietly counted as help.

Making the Confirmation Step Feel Serious

The confirmation page is the only place where a person turns a guess into a fact the system will remember.

I wanted it to feel slightly weightier than a normal form.

Three important decisions shaped this screen:

  1. Explicit confirmation: The submit button is disabled until a checkbox is ticked that reads "I confirm this resolution is accurate".

  2. Transparent retention: A side panel says what will be retained: the confirmed cause, the fix, the verification, and nothing else.

  3. Honest success reporting: If Hindsight accepted the record, the page says "Retained in Hindsight". If not, it says the resolution is saved but not yet remembered and offers a retry button.

That last branch matters.

A success screen that always says "Done" is a lie whenever a downstream service fails.

Honest Empty, Loading, and Error States

I spent more time on states than on the happy path.

Loading spinners use plain-language labels such as "Loading incident…".

When the backend is unreachable, the interface displays a message that names the problem, such as "Is it running on port 5000?"

An empty Historical Memory page explains how to create the first memory.

The sidebar shows a real count of retained memories, and a connection indicator turns red when the health check fails.

Every number on the dashboard comes from the API.

There is no placeholder data hiding behind the pastel colours.

What Went Wrong

My first version used a dark navy theme and read like a typical monitoring console.

It was fine and forgettable, and the memory card was the same visual weight as everything else, which buried the thing the product is about.

Moving to a lighter palette and giving memory-related elements a single lavender accent made recall visible at a glance.

I also ported a design that had been prototyped with placeholder text: a made-up greeting, sample incident IDs, and a hardcoded memory count.

Before shipping, I replaced every one of them with data from the API and searched the code for numbers that did not come from a request.

Anything displayed on a dashboard should have an obvious source.

One limit I have not fixed: the small text sizes inherited from the original design are hard to read on some laptop screens, and the layout is tuned for desktop first.

A proper accessibility pass (contrast ratios, keyboard order, screen-reader labels on the stepper) is still ahead.

Lessons Learned

Building OpsRecall reinforced five lessons about designing interfaces for AI systems.

1. Show the evidence next to the claim.

If an AI cites memory, put the memory on screen.

2. Distinguish verified from merely retrieved.

Two visual states are cheap and prevent overtrust.

3. Build the comparison, not the pitch.

Before-and-after views convince more than adjectives, and they also show where memory does nothing.

4. Make success screens honest.

Report what actually happened downstream, not what you hoped.

5. Check for fake numbers before you ship.

Placeholders hide well in good-looking layouts.

Final Thoughts

The biggest lesson from building the OpsRecall frontend is that AI memory should not be an invisible feature.

If an assistant remembers a previous failure, the engineer should be able to inspect what was recalled, where it came from, and whether the information was verified.

Hindsight makes it possible to retrieve relevant information from previous incidents. The frontend's responsibility is to make that retrieval understandable and useful.

For background on why persistence across sessions changes what an assistant can do, Vectorize's explanation of agent memory is a good starting point.

Project Links & Resources

OpsRecall — Live Application

https://opsrecall-mvci.onrender.com/

OpsRecall — GitHub Repository

https://github.com/SahanaK73/OPSRECALL

Hindsight — Official GitHub Repository

https://github.com/vectorize-io/hindsight

Hindsight — Official Documentation

https://hindsight.vectorize.io/

Vectorize — Understanding Agent Memory

https://vectorize.io/what-is-agent-memory

An AI assistant should not just remember the past. It should show the evidence behind what it remembers.