Designing an AI wealth manager that tries to make you check it less
A design study, not a shipped product. Aria is a personal AI wealth manager I specified, architected and partially built. The foundation exists and the product layer does not, and the interesting part is which half got built and why.
| What it is | A goal-based wealth manager for ordinary savers, with an AI advisor whose main job is talking people out of selling |
| Status | Foundation built, product layer not implemented. Private repository |
| Built | Data model, migrations, repositories, a contract-first market-data adapter, task infrastructure. 69 tests passing |
| Not built | Every API route is a stub. There is no advisor. No user has ever used it |
| Why it's here | The product thinking is the output worth showing — and auditing it a year later turned up a lesson I now apply everywhere |
I am putting this up because the specification is the part I would want to be judged on, and because a portfolio that only shows finished things quietly misrepresents how work actually goes.
The thesis
Most consumer investing products are built to be opened. Engagement is the metric, so the product optimises for it: notifications, streaks, daily balances, a feed.
For a long-term wealth product that incentive is inverted. The evidence is unambiguous that the largest destroyer of retail returns is not fees or bad allocation but behaviour — selling in drawdowns, chasing last year's winner, trading too often. A product that successfully increases how often you look at your portfolio is a product that makes your outcomes worse.
So Aria's design premise was stated in one line and everything else derived from it:
The most sophisticated thing Aria can do is help someone do nothing when markets are crashing, because that is what builds wealth.
This is the whole product. The allocation logic is a solved problem, and deliberately boring — a low-cost, three-fund, risk-matched portfolio beats most of what is sold to retail investors. The hard part is the twenty minutes on a red morning when someone wants to sell, and that is where the AI belongs.
Designing against engagement
The consequence is a success-metrics table that reads backwards, and it is the artifact I would point an interviewer at first:
| Measured | Explicitly not measured |
|---|---|
| Users staying invested during downturns | Daily active users — checking often is the failure mode |
| Goal achievement rate | Trades executed — fewer is better |
| Time to goal completion | Short-term returns — noise, not signal |
| Behavioural mistakes prevented | Session length |
Writing down what you refuse to optimise is harder than writing down goals, and more useful. It resolves arguments before they happen: any proposal that would raise daily actives at the cost of trading frequency is already decided.
The same discipline produced a "what this does not do" table alongside the feature list — no day trading, no options, no market timing, no performance chasing — each with the reason. A feature list without its negative is a wish; with it, it's a scope.
The course correction
Aria did not start clean. It began by inheriting architecture from an earlier trading system of mine, and the inheritance brought the wrong assumptions: short holding periods, signal generation, an active-management cadence, a decision loop shaped around "what should I trade today?"
That is a plausible way to build the wrong product quickly, and it is worth being precise about why. The trading architecture was not badly built. It was correctly built for a different user with a different time horizon, and almost every structural choice that was right there was wrong here. Reusing it would have produced a wealth manager with a day trader's nervous system.
So I wrote a course-correction document that did nothing except restate the identity: what Aria is, what it is not, and what changes as a result. Time horizon years to decades rather than hours. Monthly rebalancing rather than continuous signals. The user relationship as advisor and coach rather than terminal. The recurring decision as "should I adjust anything this month?" rather than "what do I trade today?"
The document changed no code. It changed every subsequent decision, which is cheaper.
What the architecture committed to, and what it actually did
Here the study turned into an audit, and the audit is the most useful thing in it.
Aria has a decision log: six architectural decisions, each with a rationale and a stated impact, written at the time. Reading it a year later, three of them describe a system I would be pleased to have built.
- Tenant isolation enforced by Postgres row-level security, forced at the table level, so one forgotten
WHEREclause cannot show one person another person's finances. - Configuration that validates at startup and fails loudly, aggregating every problem into one error instead of surfacing them one missing variable at a time.
- Structured logging with request-id injection and sensitive-data filtering, human-readable in development and JSON in production.
None of the three exists in the code. There is no row-level security in any migration and no current_user_id anywhere. There is no configuration validator and no validation logic in the settings module. There is no logging configuration module at all. The project-state document lists all three under "what is working now."
I did not know this until I went looking. The decision log recorded the decisions as made and applied, and nothing ever checked.
This is the same failure my other systems are explicitly built to prevent, arriving from the opposite direction. There, the recurring bug was a producer being retired while its consumer was left behind. Here it is a decision recorded as done that was never done — a document drifting away from the code it describes, silently, because a decision log is written once and read rarely, and nothing tests it.
The honest conclusion is not that the decisions were wrong. Row-level security is still the right call for multi-tenant financial data, for exactly the reason the log gives. The conclusion is that a decision log is not evidence, and I had been treating it as evidence. The fix is the same one I use elsewhere: every claim either carries a test that fails when it stops being true, or it is labelled as intent rather than state.
What the architecture actually did do
One commitment was built, and built properly: a contract-first boundary to the market-data provider. Nine modules — client, cache, rate limiter, schemas, typed errors, configuration, dependency wiring — written against the provider's published contract.
That one is verifiable, and the proof showed up while I was writing this: no API route in the application touches market data. Zero references across all eleven route modules. The dependency on a private upstream platform is real, but it is contained behind one adapter, which means the product layer could be built — and the provider swapped for something public — without the two ever meeting.
That is the difference between a decision that was implemented and a decision that was recorded.
What was built, and what was not
Honest, because the point of the study collapses otherwise.
| Layer | State |
|---|---|
| Data model, migrations | Built |
| Repositories and schemas | Built |
| Market-data adapter, caching, rate limiting | Built |
| Background task infrastructure, scheduling | Built |
| Configuration module | Built (without the validation its own log claims) |
| Row-level security, config validation, structured logging | Documented as done. Never implemented |
| Test suite over the above | 69 passing |
| Goals, portfolios, recommendations, reports | Stubs |
| The AI advisor | Not started |
| Any user interface | Not started |
The internal state document tracked this accurately throughout — it says "Phase 1, data layer" and never claimed otherwise. The repository README was more optimistic than the project was, which is its own small lesson about where overclaiming creeps in: not in the tracking documents that nobody reads, but in the introduction that everybody does.
What I take from it
The foundation is the expensive half and I finished it. Models, migrations, isolation, adapters and task infrastructure are where projects like this usually die. That work is done and it is good.
The product layer is where the judgment lives, and I stopped before spending it. The advisor — what it may say, what it must refuse to say, how it behaves when someone wants to sell at the bottom, how it avoids becoming a chatbot that congratulates you for checking — is the entire differentiator and none of it exists. Given the choice again I would build one thin end-to-end slice through the advisor before completing the data layer, precisely because the advisor is the risky part and the data layer is not.
Writing the negative space was the highest-return hour spent. "What this does not do" and "what we refuse to measure" did more to shape the design than any architecture diagram, and they were the cheapest documents to produce.
And a decision log needs a test, or it becomes fiction. Three of six decisions here describe code that does not exist, and the document that tracks project state repeated the error. I found it only because I went looking a year later. Everything I have built since carries the rule that came out of this: a claim is either enforced by something that fails when it stops being true, or it is marked as intent. That rule has an origin, and this is it.
The behavioural thesis is the piece I would build a company on. The code is a foundation waiting for it.