Skip to content
Work

Picton MahoneyPrivate capitalLive since Aug 2026

Picton Mahoney runs a discovery and review engine written for one firm and no other

Picton Mahoney runs a bespoke discovery and review engine: its own repository, sixteen route segments in the portal, and two faults we found by using it.

16route segments written for this client and no other
Build artefact

Found by diffing the client's repository against the platform and every other client.

111commits on an engine repository that forks nothing
Build artefactAug 2026
31pull requests merged into the portal
Build artefactAug 2026

Key highlights

  1. 01An engine in its own repository, plus sixteen route segments grafted into the client's portal.
  2. 02Two production faults found by using the client's own product, both root-caused and fixed.
  3. 03One of five invited users signed in inside thirty days. We print that.

The build

Platform, then what was written for this client alone

Shape

A fork of the platform, alongside a separate application of its own

Arrived in the box
500
database migrations
69
built-in skills
17
provisioning steps

Build artefactAug 2026

Written for this client

14route segments that exist in this client’s repository and in no other, and not in the platform

  • (dashboard)/intelligenceThe review queue: everything the engine has found, waiting on a person
  • (dashboard)/intelligence/[id]One firm, its score, the reasoning behind the score, and the decision
  • (dashboard)/intelligence/advisorsThe same review flow for a second population
  • (dashboard)/intelligence/pensionsAnd a third
  • api/intelligence/reviewApprove, reject, or send back for more information
  • api/intelligence/review/[id]One decision, with the note that explains it
  • api/intelligence/review/batchA decision applied to a set, because reviewing one at a time does not scale
  • api/intelligence/review/bulkThe same again for a whole page of results
  • api/intelligence/runsEvery run the engine has made, and what it cost
  • api/intelligence/runs/discoveryStart a run
  • api/intelligence/runs/statusWatch one without refreshing the page
  • api/intelligence/exportApproved rows leave the portal for the system the team already works in
  • api/intelligence/export/advisorsThe same export for the second population
  • api/intelligence/export/pensionsAnd the third

Found by diffing this repository against the platform and every other client

Before

  • 01The work existed. It was a person, a browser, and a spreadsheet.Sourcing was done by hand, one firm at a time, and the judgement that mattered was buried under the hours of looking that came before it.
  • 02Nothing recorded why a firm was passed over, so the same firm got looked at again.
FIG. 01
What was built, and who used it. Source: repository history and the tenant user table, read 2026-08-20.
ItemValue
Route segments written for this client16
Commits on the engine repository111
Pull requests merged into the portal31
Invited users active in the first 30 days1 of 5
First commit to go-live6 weeks
What was built, and who used it. Source: repository history and the tenant user table, read 2026-08-20.REV 2026.09

Situation

The judgement was the job. The looking was the cost.

Picton Mahoney is an employee-owned asset manager. The work in question is the kind that every firm like it does and almost none of it enjoys: find the organisations worth a conversation, work out which ones are actually worth a conversation, and then have the conversation.

The first two of those are not judgement. They are hours. Someone opens a browser, reads, decides, writes a row in a spreadsheet, and does it again. The judgement that the firm is actually paying for arrives at the end, after the part that anyone could do.

And because the looking lived in a browser and a spreadsheet, the deciding did not survive. A firm that had been considered and passed over in March looked exactly like a firm nobody had ever opened, in June.

What we built

An engine in its own repository, and a place to argue with it

Two things, in two repositories, for two different reasons.

The engine is not a fork of our platform. It has its own repository and its own release history, because what it does is specific to one firm and grafting it into a product every other client rides would have been the wrong shape. It runs discovery, enrichment and scoring as one pipeline, and it writes what it finds somewhere a person can look at it.

The portal is a fork of the platform, and sixteen route segments were written into it that exist in no other client's copy and not in the platform itself. We know it is sixteen because it was counted, by diffing this repository against the platform and against every other client, rather than estimated.

Those sixteen are almost all one idea: a queue, and everything a person needs to make a decision in it and have that decision stick. Pending, approved, rejected, and needs more information. A score with the reasoning behind it, so the number can be argued with rather than obeyed. Sort and filter, because a queue you cannot re-order is a list. Decisions in bulk, because reviewing a thousand things one at a time is not review, it is data entry. And an export, so an approved row leaves the portal and arrives in the system the team already works in.

How it runs

The engine proposes. A person disposes.

Nothing the engine finds is treated as a conclusion. A run produces candidates and a score, and the score carries its reasoning, and both of those sit in a queue until a human being agrees or does not.

That ordering is deliberate and it is the whole design. An engine that decided would be wrong in ways nobody could see. An engine that proposes is wrong in ways that show up in a review queue, get rejected with a note, and change what the next run does.

We also took things out. The platform ships a new-user setup flow, a welcome modal and a product tour, all of which are useful for a company meeting the product for the first time and none of which belong in front of a team who were handed a tool and told to use it. They were suppressed in three separate layers. The education section came out of the navigation entirely. The workflows page leads with the engine rather than with a generic example.

What we measured

The build, and the part that has not worked yet

16
route segments written for this client and no other

Sixteen route segments in the portal. One hundred and eleven commits on an engine repository that is not a fork of anything. Thirty-one pull requests merged. A ten-page branded guide and a help centre inside the product. Six weeks from the engine's first commit to go-live.

And one of five invited people signed in during the first thirty days.

We are printing that second number next to the first ones because leaving it out would make this page a lie of omission. Every deliverable was accepted. The engine runs. Most of the people it was built for have not opened it. That is a delivery failure and it is ours, not theirs, and the fix for it is not more code.

If you want the version of this story with a measured outcome at the end of it, read the knowledge brain case: eight of ten people used that one in its first month, and we can show you what they asked it. If you want to know how we would approach a build like this one, the audit is three to five days and ends in a plan with the arithmetic attached.

What broke

The default tab crashed once the queue filled up.

Root cause
The query asked for its rows by listing every identifier in the URL. Below a few hundred rows that is ugly and works. Past that it overran the URL length limit and the request never arrived. The smaller tabs rendered perfectly the whole time, which is why it read as intermittent rather than as a ceiling.
Fix
The read is chunked, so the queue is paged rather than demanded in one breath.
How we know
The tab that had been failing rendered its full contents. The underlying cap is documented in the pull request that fixed it, because the next person to write a large read needs to know the ceiling exists.

Every page of the help centre crashed in the browser, and every check we had said it was fine.

Root cause
One date in a document's frontmatter was written without quotes, so it parsed as a date object rather than a string and was rendered directly. That throws during hydration, in the browser, after the server has already sent correct HTML.
Fix
The value is quoted, and the renderer coerces rather than trusting the parse.
How we know
Opened in a real browser rather than fetched. That is the point of the failure: a raw fetch of the same URL returned a clean 200 and a correct page the whole time it was broken for every human being.

How it was delivered

  1. 01The engineJun 2026
    • A repository of its own, not a fork of the platform
    • Discovery, enrichment and scoring as one pipeline
  2. 02The portalJul 2026
    • Sixteen route segments grafted into the client's fork
    • A review queue with sort, filter and bulk decisions
    • Approved rows exported to the system the team already uses
  3. 03Go liveAug 2026
    • Milestone one accepted
    • A ten-page branded guide and an in-portal help centre
James Booth
Engineer of record

What we did not measure

We did not measure what the engine is worth to the firm. What it is pointed at, how it decides and what it has found are the client's to state and not ours, so the only numbers here are the ones about the thing we built. The honest gap is adoption: the engine runs, and most of the people invited to it have not opened it yet.

1 of 5invited users who signed in during the first 30 days
System loggedAug 2026

What to take from this

If a surface will hold thousands of rows, write the read that way on the first day. A query that lists every identifier in the URL works beautifully in testing and fails silently at the exact moment the product becomes useful.

A fetch is not a browser. A page can return 200 with correct HTML and still be broken for every human who opens it, because hydration runs somewhere your check does not.

Shipping is not adoption. We delivered a working engine and then watched four of five invited people never sign in, which is a delivery failure even when every deliverable was accepted.

Start free. Know your number in five days.

A 3 to 5 day audit of your operations, ending in a plan with the ROI math attached. No obligation.

5x ROI in 30 days. Or we work for free.