A brief that
grades itself.
The Morning Ledger is an automated daily market brief. It has run every morning since August 2026. It reports on a real portfolio, pulls from market and coursework APIs, and keeps a scored record of every prediction it makes — including the ones it gets wrong.
A failure with no error message
The first version ran on a 9am cron job. By every measure I was watching, it worked. It fired on schedule, it never crashed, and a complete brief landed every morning.
It was also getting quietly worse every single day.
The brief pulls from sources that need an authenticated browser session. On an unattended run, that session was not available — so every one of those sources silently fell back to a thinner substitute. The output still had all eight sections. It still read like a finished document. Nothing anywhere in it said the data behind half the page had been downgraded.
This is the part I think about most. A crash would have told me something was wrong on day one. A silent degradation ran for weeks.
The principle that came out of it
A fallback that does not announce itself is worse than a visible failure. Every degraded path in the system now labels itself in its own output.
Change when it runs, not how it fails
The instinct is to add error handling. But there was no error to handle — every component did exactly what it was told. The problem was the condition the job ran under.
So the trigger moved off the clock entirely. Instead of firing at 9am regardless, it now fires on the first session I open each day, which is precisely the condition the authenticated sources need. The scheduled job stayed on as a safety net — but guarded, and it labels its own output as the degraded edition.
Two execution paths, one source of truth
first session of the day · attended
late afternoon · labels itself degraded
The fallback does not carry its own copy of the steps. It invokes the same one the primary path does, so there is no second version to drift.
Two flags, two questions
A once-a-day job is harder than it sounds when the runtime can start, resume, and restart unpredictably. The system tracks two separate markers, because they answer different questions and one flag cannot do both jobs.
FIRED— the trigger already ran today- stops it nagging
PUBLISHED— a brief actually went out- stops duplicates
The marker is written before the work, not after. A crashed run loses that day rather than risking a retry loop — a deliberate trade, because a missed brief is cheap and a nag loop is expensive.
It writes down when it is wrong
Any claim specific enough to be checkable gets logged with a horizon date and the full reasoning behind it — recorded before the outcome is known. When a later run passes that horizon, it scores the call and writes one retrospective line: what was claimed, what happened, and which mechanism was misjudged.
A system that only ever forecasts forward has no way to be wrong, and a tool that cannot be wrong is not telling you anything.
An actual entry, unedited
Half right. Direction was right, magnitude was badly wrong: predicted −1% to −2%, actual −0.05% — a 20–40× overestimate. Cause: sized the print as if it landed into an unchanged backdrop, but dovish comments 24h earlier had already moved the odds from ~65% to ~55%, and the strong print only pushed them back to ~50%. The two events largely cancelled.
Lesson: size the delta versus what is already priced, not the absolute surprise.
On the record itself
At the time of writing the directional log holds six graded calls, three correct. That is not a track record — it is a sample far too small to separate skill from a coin flip, and it is published unedited for exactly that reason. The point of the loop is that the misses get written down and diagnosed, not that the calls are good.
The loop has also caught its own bookkeeping twice: once when a run graded a row against the wrong session's close, and once when an after-hours quote was logged as an official close. Both produced a written guard rather than a silent fix — grade each row against its own target date; take closes from the settled daily bar, never a live quote.
What it taught me
Silent degradation is the expensive failure. Systems that crash get fixed. Systems that quietly return worse answers do not, because nothing raises a hand.
Sometimes the bug is the schedule. The fix here was not a code change at all — it was recognising that the job was running under the wrong conditions.
Write the reasoning down before the outcome. It is the only way a retrospective means anything. Grading a call you reconstructed after the fact grades your memory, not your judgment.