Journal · Build notes

Journal

Day-to-day notes: what I build, what I decide and what I learn. Not over-polished — the real work, raw. These aren't essays; that's what the Decision Logs are for.

I didn't hardcode the 100 floors

In a personal roguelike I chose not to author its ~100 floors by hand. A domain service generates them from a deterministic seed: it assembles data pieces and difficulty rises through curves in JSON, not code. The wink is technical — a seed reproduces the exact run, the same reproducibility I use to rebuild a production incident or replay a transaction.

The takeaway: the rigor that makes a critical platform predictable —determinism, data-driven behavior, and decisions logged as ADRs— doesn't change with the domain. Applied to a hobby game it's the same one that avoids 3am surprises in banking. The discipline travels; the domain is anecdote.

Code snippet: a floor is generated deterministically from a seed, with rules living in data (JSON)
A floor = f(seed, altitude): deterministic and data-driven.

An output-token cap is a limit, not a spend

TrainO's photo food analysis got almost 2x slower without us shipping a single deploy. The code didn't change. The model provider's behavior did: it started returning pretty-printed JSON. On multi-item plates, the response started hitting the 1024 output-token limit, and every truncated response triggered a retry on the more expensive model. Result: escalations went from 0% to 29% in a few days.

The lesson was simple: an output-token limit is not necessarily a cost-saving mechanism. Squeeze it too tight and you don't reduce cost: you just turn an incomplete response into a full second attempt — slower and more expensive.

The fix was less glamorous, but more correct: we raised the output cap, forced minified JSON (30–50% fewer output tokens), and stopped discarding truncated responses — we now salvage the complete items before deciding whether escalation is needed. We also added per-record traceability: timings, outcome, model used, fallback applied and truncation cause. Next time, the diagnosis won't be intuition. It will be a SQL query.

Diagram of the food-scan pipeline, before and after: before, pretty-printed JSON blows past the 1024 output-token cap, parsing fails and the job escalates to the expensive model until it fails; after, with a 2048 cap and minified JSON, the same plate returns in 8.1 seconds with no escalation
The full chain: a provider-side format change plus a tight cap turned into truncation, expensive retries and double the latency — with no deploy of our own.

The test wasn't flaky: it was the calendar

Two regression tests failed intermittently. The easy read was random flakiness, but in isolation they failed the same way all day: not random — deterministic per date. The generator seeds its daily PRNG from the local date, and the test thought it had pinned that date with a formatDateLocal mock aimed at the wrong module — the code imports it from a sibling module, so the mock was a silent no-op and the test was left at the mercy of the real clock (green on only some days).

Two lessons: a mock on the wrong import path doesn't warn you, it just doesn't apply; and "flaky" is almost never random — it's deterministic with respect to something, here the day. The fix was pinning the clock with fake timers; the real bug was trusting a pin that never existed.

Terminal capture: the explicitSelection test run under 12 pinned dates, green (PASS) on only 3 and red (FAIL) on the other 9
The same test under 12 dates: green on only 3 of 12. Same commit; the only thing that changes is the clock's date.

A small LLM doesn't know what it doesn't know

In VaultOS, the command bar uses a local 3B model for phrases like "give me the first password I saved" or "my weakest password" — and it kept returning the same item. The cause: the model has no clock and no notion of password strength, and when the data doesn't exist an LLM doesn't say "I don't know" — it guesses.

The fix wasn't prompt engineering, it was determinism: I detect the intent with rules, order the candidates so item 1 is the right answer by construction, and quality phrases resolve against the real security audit — with an honest null when nothing matches. The model only picks from a list that no longer lets it be wrong.

VaultOS search bar with the phrase 'dame la contraseña más débil' and the local AI suggestion below: Open · Cpanel
The phrase matches nothing literally; the local AI resolves it against the audit and suggests the weak item.

TrainO: from a herniated disc to an expert system

I've trained my whole life. Until a herniated disc stopped me and I was told I'd never train again. I didn't take it as a verdict: I took it as a problem to diagnose. I studied and broke down load, recovery and progression until I understood it —and I came back. TrainO was born from that.

That's why TrainO doesn't hand out templates: it reasons. It computes a readiness score every day —sleep, accumulated fatigue, nervous-system state— and adjusts volume and rest. Every session respects your real recovery, not a fixed calendar. It's the system I wish I'd had.

And it isn't one feature: it's a full system —training, hybrid, GPS cardio, a science-based nutrition engine (six rules citing real studies, metabolic-adaptation detection), an autonomous Apple Watch app, coach mode and a nutritionist portal. The challenge was keeping all of it coherent: a system, not a pile of features.

VaultOS: local AI over an encrypted vault

VaultOS reached a functional MVP on Mac: a local-first vault where the entire database is encrypted with AES-256-GCM, the key is derived with Argon2id, and unlock can go through Touch ID.

What I like most: it runs on-device local AI, without a single byte leaving the machine. No cloud, no telemetry. 172 tests cover the crypto and data core. Privacy by architecture, not by promise.