Work · Products · 09

Witness

A photograph of a returned gearbox part becomes an ISO failure record linked to the batch that made it.

2ISO catalogues classified against
3Axes per record
0.70Confidence gate
0Synthetic defect images
Type
Products
When
2026 · shipped
Role
Sole designer and developer. Schema, tenancy, anomaly model, classifier, console, drawings.
Scale
Multi-tenant on Postgres with row-level security · two part families · twelve routes

In plain language

A returned gearbox part arrives with a complaint and almost nothing else. Someone photographs it, writes one sentence in a spreadsheet, and puts the part in a bin. When the same damage appears on a later batch, nobody can prove it, because the first record said "worn" and the second said "pitting" and neither cited a standard. Witness makes the record the product. A photograph becomes a record with a damage mode, a clause number, a severity, a cause and a link to the batch that made the part. The model suggests. The inspector decides.

The question

The failure was never the hard part. The record was. How do you turn a photograph and a one-line complaint into evidence a warranty claim can stand on?

Rough sketch

Where things sit on the screen, and in what reading order. Drawn, not screenshotted, so it stays true when the styling moves on.

Review queue

Finding detail

Insights cube

The review queue is the product's centre of gravity. It is the only screen where a person changes what the system believes.
The pipeline drawn as a machine. Four modules feed a confidence gate valve. The two outputs, auto-filed records and reviewed records, converge on the fleet insights cube.
The confidence gate is the only place the software decides whether a person is needed. Above the threshold a record files itself; below it, an inspector rules.

The record is the product

A returned part arrives with a complaint and little else. A person photographs it. A person writes one sentence in a spreadsheet. The part goes in a bin.

Months later the same damage appears on a different batch. Nobody can prove it. The first record said “worn”. The second said “pitting”. Neither cited a standard, so neither is evidence.

The failure was never the difficult part. The record was.

Two standards, three axes

Witness reads two part families, and each has its own published catalogue. Rolling bearings are classified against ISO 15243, which has six primary damage classes running from rolling-contact fatigue to fracture and cracking. Gear teeth are classified against ISO 10825, which covers the flank, the root and the mesh line.

Each finding carries a clause number, so it traces back to the document rather than to an opinion.

The third axis is attribution. A damage mode says what the surface looks like. An attribution names the mechanism that caused it. The same pit can be fatigue at end of life or a contaminant dent that started it early. The surfaces look alike. The fleet action does not, so the mechanism is recorded separately rather than folded into the mode.

Cutaway of a deep-groove ball bearing. Callouts mark the outer raceway, the rolling elements, the inner raceway and the bore, each with its ISO 15243 damage classes.
A spur wheel driving a smaller pinion. Callouts mark the tooth flank, the tooth root and the mesh line, each with its ISO 10825 damage classes.
The gear side. ISO 10825 covers the flank, the root and the mesh line: wear, scuffing, contact fatigue, cracks and tooth fracture.

Where the software stops deciding

The pipeline runs in stages and each stage does one job. Intake scores provenance and catches a duplicate. Enrolment learns what a good part looks like from the tenant’s own photographs. Stage one asks only whether a part is abnormal. Stage two picks one code from the catalogue.

Then the confidence gate. Above 0.70 the record files itself. Below it the record waits for an inspector. That gate is the single place where the software decides whether a person is needed, and the threshold is on the page rather than in a config file.

After the gate, the person decides. The system stores the model’s suggestion beside the human determination and never overwrites it. Analytics and warranty reports read the human call. A disputed classification stays auditable, which is the only reason any of this is worth storing.

Exploded assembly of a deep-groove ball bearing: the outer race, the cage, the rolling elements and the inner race spread along the shaft axis, numbered one to four.
The parts the model reads. The rolling elements and the two races carry the ISO 15243 damage; the cage and the shaft seat decide how load runs through them.

The loop closes on the inspector

Every confirmed record becomes a pair: what the model said, and what the inspector said. That pair is the label export, and it is the training set for the version of stage one that does not exist yet.

This is stated as a limit rather than hidden. Stage one today is a memory bank of normal patch features scored by nearest-neighbour distance. It has the shape of PatchCore without the backbone, because torch does not fit inside a serverless function. A trained diagnostic model needs labelled data, and labelled data is what the review queue produces.

There is no synthetic defect data anywhere in the system. A generated defect teaches the model the generator.

Stack

Next.js on the App Router with React and Tailwind, Supabase for Postgres, row-level security and authentication, and a stateless Python function for the anomaly score. Stage two runs keyless by default on a deterministic heuristic and routes to a vision model when a key is present. The confidence gate decides where a finding lands, not the source of the suggestion.

How it is put together

Left to right, the path a request or a record takes through the system.

  1. 01

    Intake

    • EXIF read
    • Provenance scored
    • Perceptual hash against the tenant's assets
    • A re-sent photo is caught as a duplicate

    Before anything is believed.

  2. 02

    Enrol

    • The tenant's own known-good photos
    • A coreset is trained from them
    • Threshold calibrated leave-one-image-out

    Per part family, per tenant.

  3. 03

    Detect

    • PatchCore-style memory bank
    • Nearest-neighbour distance
    • Anomaly score and heat map
    • No database credentials in the function

    Abnormal against normal, nothing more.

  4. 04

    Classify

    • ISO 15243 for bearings
    • ISO 10825 for gear teeth
    • An off-list code is discarded
    • Confidence gate at 0.70

    A forced choice, never free text.

  5. 05

    Decide

    • High confidence files itself
    • Low confidence queues for review
    • Inspector confirms or corrects
    • Model suggestion kept beside it, immutable

    The human call is authoritative.

  6. 06

    Attribute

    • Mode, severity and cause as three axes
    • Finding linked to batch and supplier
    • Warranty report
    • Label export as model and human pairs
The confidence gate is the one place the software decides whether a person is needed. Everything before it is a pipeline stage that does one job and hands off. Everything after it is a human decision that the system records and never overwrites.

What happens, step by step

In plain language, in the order it happens.

  1. Read the photograph before trusting it

    EXIF gives a provenance score. A perceptual hash is checked against the tenant's other assets, so the same part photographed twice and sent twice is recognised rather than counted twice.

  2. Learn what good looks like

    Enrolment is the tenant's own photographs of undamaged parts, per part family. There is no synthetic defect data anywhere in the system, because a generated defect teaches the model the generator.

  3. Ask only whether the part is abnormal

    Stage one is a memory bank of normal patch features scored by nearest-neighbour distance. It answers one question. It does not name the damage.

  4. Force the answer onto a published standard

    Stage two picks one code from the part family's ISO catalogue. It cannot invent a code, because an invented one is discarded rather than stored. The finding carries a clause number, so it traces back to the document.

  5. Send the unsure ones to a person

    Above the gate the record files itself. Below it the record waits in the review queue. The threshold is visible on the page rather than buried in a config file.

  6. Record the mechanism, not only the surface

    The same pit can come from fatigue at end of life or from a contaminant dent that started it early. The damage mode looks identical. The batch-level action does not, so attribution is a separate axis.

  7. Close the loop

    Every confirmed record becomes a model-and-human pair in the label export. The training data is the inspector's work, collected as a by-product of doing it.

Decisions, and what they cost

Every choice worth recording has a road not taken. Both are here.

  • Classify against ISO 15243 and ISO 10825

    instead ofA house taxonomy of damage names

    A house taxonomy is a private opinion. A warranty claim needs a clause number from a published standard, and two inspectors using the same catalogue disagree far less than two inspectors writing prose. The standard also makes the record portable to a customer who never saw this tool.

  • The inspector's decision overrides the model, permanently

    instead ofA model that improves itself on its own predictions

    The system stores the pair and keeps the suggestion immutable. Fleet analytics and warranty reports read the human call. A disputed classification is therefore auditable after the fact, which is the whole reason the record exists.

  • A visible confidence gate

    instead ofAlways route to a person, or never

    Routing everything to review wastes the inspector on the obvious cases. Routing nothing to review files a guess as a finding. One threshold, shown on the page, is the only honest version.

  • A memory bank with cheap patch statistics

    instead ofA deep backbone

    Torch does not fit the size limit of a serverless function. The shape of PatchCore survives without it: a memory bank of normal features, a nearest-neighbour score, and a threshold calibrated leave-one-image-out. This is stated as a limit in the model card rather than dressed up as a diagnostic model.

  • ISA-101 discipline in the console, warm paper on the public pages

    instead ofOne palette everywhere

    A high-performance HMI keeps the ground near-neutral and spends saturated colour on abnormal. A marketing page has the opposite job. Two regimes, one product, and the console never borrows the brochure's colours to mean something operational.

  • Severity as a hue, a glyph and a numeral together

    instead ofSeverity as a colour

    Colour alone fails under colour-vision deficiency, greyscale printing and forced-colours mode, and a warranty report is printed more often than it is read on screen. One component enforces the pairing, so the rule cannot drift page to page.

  • One geometry module behind both the drawings and the app

    instead ofHand-drawn figures next to live components

    A figure drawn once and edited later disagrees with the software it documents, and nothing fails when it does. The bearing and gear drawings are generated from the same module the interface renders from.

What came out of it

  • Live, multi-tenant, with row-level security scoping every row to the signed-in workspace.
  • Two ISO catalogues classified against, with the clause number carried on the finding.
  • Three axes per record: damage mode, severity, and the cause mechanism behind it.
  • Findings link to batches and suppliers, and export as a warranty document or as JSON.
  • The label export ships every confirmed record as a model-and-human pair, so the review queue is the training set.
  • The anomaly function holds no database credentials. Feature vectors in, a score out.

Still open

  • Stage one is a memory bank, not a trained diagnostic model. That upgrade is blocked on labelled data, which is exactly what the review loop collects.
  • EfficientAD is out of scope on patent grounds, and the model card says so rather than leaving the omission unexplained.

Built with

  • TypeScript
  • Next.js
  • React
  • Supabase
  • Postgres RLS
  • Python
  • NumPy
  • Computer vision
  • Vercel