field notes · 2026-07-30 · 2 min
I Taught My Investment Agent to Refuse
The most important function in my new portfolio agent is the one that throws an error when the output starts sounding like advice. On designing the boundary before the feature.
This week I added an investment-strategy agent to DavidOS. It tracks my portfolio against a thesis I wrote, prices positions with live quotes, and reports lane weights and concentration every morning.
Here's the function I'm most proud of. It runs last, right before the report is written to disk:
BANNED_OUTPUT_PATTERNS = (
"you should buy", "you should sell", "i recommend",
"price target", "overweight", "underweight", "trim to",
)
def _assert_advice_boundary(text: str) -> None:
hits = [p for p in BANNED_OUTPUT_PATTERNS if p in text.lower()]
if hits:
raise SystemExit(f"ADVICE BOUNDARY VIOLATION — {hits}. Refusing to write.")
If the generated report ever drifts into recommendation language, the agent doesn't soften it. It refuses to produce the report at all.
Why build a wall against yourself?
Because the failure mode of an "AI investment assistant" isn't bad stock picks. It's the slow slide from measurement to suggestion. It starts with arithmetic — "your largest position is more than half the account" — and six prompt-tweaks later it's "consider trimming that position," and now a text generator is making financial decisions for a household.
I wanted the opposite: an instrument. A speedometer, not a chauffeur. So the boundary went in first, as code, before the features. The config file has an [advice_boundary] section where every advice capability is set to false — and if a future me flips one to true, the runner exits with an error explaining that this agent doesn't implement an advice mode. The setting isn't a switch; it's a tripwire.
The interview was the actual product
The agent enforces a thesis. But whose? An LLM will happily invent investment conviction for you, and it'll sound great. That felt like the real danger — a system that manufactures beliefs and then holds me accountable to beliefs I never had.
So instead of generating the thesis, the system interviewed me for it. Structured questions, my answers recorded verbatim, and — the part I'd recommend to anyone — a falsification condition for every position. Not "why do you like this stock" but what specific, observable event would prove you wrong. Under a sell-only-on-broken-thesis rule, those falsification lines are the entire sell discipline.
The interview also surfaced something I hadn't noticed: two of my holdings are, structurally, the exact scenario that would break my largest holding. The system didn't resolve that tension for me. It wrote it down, labeled it an open question, and left it where I'd have to keep seeing it.
The pattern
Every section marked "the human decides" in my vault is marked that way in the machine's own config, not in a comment. The agent reads its boundaries from the same file I edit. That's what I mean when I say DavidOS is governed: not that I trust the agents, but that the agents are built to distrust themselves in the specific ways that matter.
Speed is cheap now. Refusal is a feature you have to design.