Series: Weekend at Claude's — misadventures in building a production app with an AI anyone could mistake for the person who's going to make the whole thing happen
Claude Code was in the middle of implementing the community coordination features — claim items, ride requests, reliability scores. It had finished making its changes, ran a type check, and was about to kick off an Android APK build. Everything looked good from its perspective.
Except there were errors.
Two of them, at lines 47 and 706 of ListDetailScreen.tsx. Claude Code looked at them, concluded they were pre-existing — not caused by anything it had just done — and was about to proceed with the build anyway.
I caught it before the build started.
Before we compile, are we fixing the errors at lines 47 and 706 of ListDetailScreen.tsx? If not, can we attempt to fix that before we compile?
One message. That's all it took.
What the errors actually were
ListDetailScreen.tsx was missing an import — ItemStatus wasn't being pulled in from the types file, even though it was being used. And StatusDot, a shared component, had its status prop typed too narrowly: it only accepted RetailStatus | HomeworkStatus, but the component's own switch statement already had a default case that handled anything else gracefully. The type annotation was more restrictive than the implementation.
Neither of these was introduced by this session's changes. They were pre-existing — the kind of thing that accumulates in a codebase over time, compiles fine in practice because the build toolchain doesn't always enforce TypeScript strictly, and gets silently inherited by everyone who opens the file afterward.
Claude Code's original read of the situation was technically correct. The errors were pre-existing. The build probably would have succeeded anyway. Proceeding was a defensible call.
But "defensible" and "right" aren't the same thing.
The fix took about two minutes
Read the types file. Confirm ItemStatus exists but isn't imported. Add the import. Widen StatusDot's prop type from a narrow union to string — the component already handles unknown statuses via its default case, so this is a one-line change that makes the type annotation match the actual behavior. Run the type check again.
The two errors from ListDetailScreen were gone. Four others remained — all pre-existing, all unrelated to this session, all in different files. Those got surfaced too: an async useEffect callback in MedicationListScreen, and SchedulableTriggerInputTypes missing from the expo-notifications type definitions in two notification service files.
None of those were blocking. The build could proceed. But now we knew that, explicitly, instead of just assuming it.
What I asked for next
After the fix, I asked Claude Code to adopt this as a standard: whenever a compile or type check surfaces errors, group them — new vs. pre-existing, blocking vs. non-blocking — and pause for an explicit check before proceeding. Fix now, skip, or defer. My call, not an assumption.
Claude Code agreed. Applied it immediately for the rest of the session.
That's the part that's useful for that session but still requires me to say it again next time.
So I asked for it to go into DECISIONS.md.
The moment it became permanent
DECISIONS.md is one of the core project files — alongside CONTEXT.md, TASKS.md, CLAUDE.md — that Claude Code reads before touching anything. When a decision goes in there, it doesn't just apply to the current session. It applies to every future session, from the moment the file is uploaded, without me having to say anything.
The entry is straightforward:
Error surfacing standard: After any compile or type-check, list all errors grouped by severity (new vs. pre-existing, blocking vs. non-blocking). Pause before proceeding. Present options — fix now, skip, defer — for each group. Do not assume permission to proceed past errors, even pre-existing ones.
One paragraph. Permanent.
The next time Claude Code opens a session on this project, it'll read that. It won't need to be reminded. It won't proceed silently past errors while assuming they're fine. It'll surface them, group them, and wait.
Why this matters more than it sounds
The errors at lines 47 and 706 weren't catastrophic. They probably wouldn't have broken the build. But they were there, and they would have stayed there, and the next set of changes would have been built on top of them, and the set after that. That's how technical debt accumulates — not in big dramatic failures, but in small decisions to proceed past things that seemed non-blocking at the time.
More importantly: Claude Code had no way of knowing whether I knew about those errors. Maybe they were intentionally deferred. Maybe I'd been meaning to fix them for weeks. Maybe I genuinely didn't know they existed. The right behavior wasn't to decide for me — it was to surface them and let me decide.
That's the actual standard. Not "fix all errors before proceeding" — that would be too slow and would frequently be wrong. The standard is: don't hide information. Show me what's there, tell me what category it falls into, ask what to do. Thirty seconds of surfacing saves hours of debugging later, because I now know what the baseline is.
Three steps from a caught assumption to a permanent process. That's the productivity leverage — not the two-minute fix, but the "never have to catch it again" that comes from writing it down correctly.
Aphilaty is a privacy-first community coordination app. aphilaty.com