Dev Journal - 2026-09-04: the demo becomes a floor, and a parallel bet asks if you could sit at the desk
One page kept getting rebuilt, twice, in the same 24 hours, and each rebuild found a real bug
by actually looking at what it drew. Alongside it, a second track asked a bigger question - not
"can a visitor watch Theo decide" but "could a visitor make the same kind of claim Theo does" -
and answered it first as a throwaway prototype before touching the real site at all. The day
closed on the loop itself doing the thing all of this was built to show: three positions closed,
a new one opened, and a demotion from SCALE to ESTABLISH that the site now had a real page ready
to explain.
The demo becomes a replay, and the replay finds a real bug before it ships
/demo (D-123, notes/028) started from a plain question: a hackathon judge cannot watch this
loop think, because the reasoning is real and already on the record - the journal writes
candidate structures, sizing decisions, every declined thesis - but it is scattered across four
pages nobody assembles by hand. The obvious wrong answer, rejected up front, was a live-looking
dashboard: connection pills and an activity feed read as busy without showing a single decision,
and the site is a static build anyway - every page already prerenders from snapshot.json,
nothing fetches at runtime. The page that got built instead picks one real decide cycle from a
rule-selected reel and replays it through the five stages how-it-works already teaches. Nothing
is generated for the page; a missing value renders not recorded, never a guess.
The join underneath the replay was the actual risk, and it was validated against the live
2,711-row journal before any Svelte was written, not after: a naive "everything since the last
outcome" window breaks on concurrent decision batches, a shape found live on 27-28 August when
two decisions were open before either closed. structures_simulated and sizing rows gained a
decision_ref at the source rather than being joined by proximity; everything older falls back
to timestamp membership, and the exporter counts and prints how often that fallback had to guess
(3 of 204 cycles, both real concurrent batches).
The bug the build found on the way mattered more than anything the plan predicted. Every page's
loader was a UNIVERSAL load, so Vite bundled the whole 898 KB snapshot into one shared client
chunk and every page downloaded it on first visit before slicing out what it actually needed -
the per-page narrowing believed to bound page weight bounded nothing (I-126). All eleven loaders
became +page.server.js / +layout.server.js; under adapter-static + prerender these still
run at build time, but now only each page's own returned slice reaches its HTML. Verified, not
assumed: the largest client chunk went from 864,845 bytes containing the snapshot's own text to
46,316 bytes containing none of it, and the rendered <body> of three pages diffed byte-identical
before and after. A second, smaller find while building the Coach section: a metrics.jsonl row
stamped a year ahead of every neighbour (I-127), still open, mitigated by ignoring any row whose
timestamp is after export time rather than trusting it for a chart.
Theo's Floor: three tabs, and a cone that shows the bet
Shipped, deployed, and then rebuilt within hours. /demo as one long scroll had ten lines of
chrome above the first real number and an explanatory sentence hung off every heading - the
content was right, the interface wasn't. The rebuild split it by the question a reader is
actually asking rather than by the order the loop's stages happen in: the loop (what is it
thinking), the book (how is it doing), the coach (is it getting better) - three tabs, each
leading with a chart instead of a paragraph.
The claim cone is the idea that made the split worth doing. A claim used to draw as a band on the
tape, which shows what was claimed but not whether claiming it was interesting. Drawn against the
lognormal cone the underlying's own implied vol projects forward, it shows the bet directly: NVDA's
3 September claim needed +/-$6.50 where the market implied +/-$12.92 over the same eight days.
Everything modelled is only drawn when the record can back it - marketFor takes the chain a
cycle actually priced, falls back to a position it opened, otherwise returns null, and most
declined cycles priced nothing, so they draw a band with no cone and a line saying why. The
probability line prefers the agent's own recorded p_band, the number it actually gated on, over
anything recomputed on the page.
Two defects found by looking at the rendered chart, not by reasoning about the code: horizonDays
was measured from the decision timestamp and returned 7 where the agent's own sense.market
recorded 8 for the same decision, which would have drawn every cone narrower than the chain it was
actually priced on - a small error with the specific effect of making Theo's bets look tighter
than they were. And the claim band collapsed to zero width on any unresolved claim, because it was
drawn from the decision forward and there is, for those, no forward yet.
The equity curve gets a readout, and the Coach explains itself
Three things the rebuilt demo still didn't answer. The equity curve had a shape and no values - it
now draws a crosshair and a readout panel (equity, return since inception, timestamp) that follows
the pointer, opt-in via a prop so the scoreboard's own copy of the same component is unchanged.
The open markers on it were unexplained dots with only a native tooltip; they're now real links to
each position's page, keyboard-focusable, and openings landing on the same tick group into one dot
with a count rather than stacking two circles on one pixel and looking like a rendering fault.
The Coach tab had been showing a posterior trace to a reader never told what a lever, a challenger,
or a posterior is. It now opens with what the Coach is allowed to touch and how a trial is scored,
then per lever: which version is live, which is on trial, the two survival rates head to head
(92.0% against 86.7% today), and a checklist of the three promotion conditions with the failing
one marked - "why hasn't this changed anything" now has a visible answer instead of an implied one.
Fixed alongside: the card had read exp.challenger.id when the id actually lives on
lever.challenger, so every trial had been rendering "? vs v0" since the tab shipped; it reads
"v1 vs v0" now.
A parallel bet: what if you could sit at the desk yourself
The same day, a plan for something bigger than a replay: /desk (notes/029), an application frame
rather than a page - draw a band and a horizon directly onto a chart, and the desk derives the
shape, runs the claim through Theo's own gates with Theo's own refusal strings, prices every
structure the incumbent catalogue supports, and sizes the one you pick through the sizer's real
waterfall. Trade it or decline it; come back after the horizon and it scores your call with the
same humility Theo applies to itself. Two findings shaped the plan before any UI was drawn: D-099
already deleted one JavaScript port of the sizing math for drifting while claiming to be verified,
so this reopens that decision only under conditions the old fork never met - fixtures Python
regenerates and fails on if stale, Node executing them on every test run, real CI. And nothing on
disk holds a live mark; the only per-position live number is P&L as a fraction of entry cost, so
the desk shows three separate clocks rather than pretending to have one.
Before touching the real site, the central bet got a throwaway answer: a single self-contained
~1,470-line HTML page (Experiment 001, SpecForge's spike format), zero framework, seeded with
Theo's real 4 September book - five real tickers at their real spots and IVs, the real
nine-family catalogue, the real MIN_BAND_EDGE gate and rejection strings - and a direct JS port
of the pricing and sizing math, explicitly not the production port and not pinned against Python.
Dragging the band toward or away from spot flips the shape badge between RANGE and BULL TARGET and
the rest in real time; the structures panel genuinely differentiates by shape, pricing different
survivors for a range claim than a directional one with real rejection strings when a claim
doesn't support a structure at all. Confirmed: the claim-first interaction holds together as a
playable loop end to end, and the single biggest finding was about interaction design, not math -
making the band itself the input, rather than a form next to a chart, is what makes the page feel
like an instrument instead of a wizard. One real defect, found in the single review pass and fixed
rather than assumed away: every em dash and typographic minus sign was rendering as mojibake with
no charset guaranteed, fixed by dropping the special characters entirely rather than trusting a
charset declaration a future edit could drop again.
What keeps being true
The rule the demo page was built to enforce on itself - draw only what the record can back, say
plainly when it can't - kept catching real bugs the moment anyone actually looked at a rendered
chart instead of reasoning about the code that drew it: a snapshot silently shipped whole to every
page, a cone one day narrower than the chain it was priced on, a claim band with nowhere to end, a
challenger card that had been naming the wrong thing since it shipped. None of these were found by
review. They were found by running the thing for real against the live record and looking hard at
what came out, which is the same discipline the trading loop is held to everywhere else in this
project.
The day ended with the loop doing exactly what three days of UI work had been building a page to
show honestly: three positions closed, a new NVDA bull call spread opened, a fresh research round
started on FLEX/GWRE/IONS/LULU/ZS, and a drawdown that demoted competence from SCALE back to
ESTABLISH - not hidden, not softened, republished straight to the site. Equity $123,931 (+23.9%),
209 theses, 115 resolved forecasts, tick 899.
Shipped: D-123 and notes/028 (the demo's first build, seven commits), the Theo's Floor rebuild
and its equity-curve/Coach-tab follow-up (two commits), notes/029's plan for /desk and Experiment
001's prototype confirming its central bet, and the day's live checkpoint. I-126 fixed, I-127
still open. 771 tests, 34 JS tests, ruff clean, every UI claim checked in the browser against the
live snapshot before being called done.