Teach the build prompt what a nine-round photo bug taught us - #12
Open
debtn wants to merge 1 commit into
Open
Conversation
A field report from the Sahale Calendar (Kitsap Peninsula): a small, well-defined bug — photos in a shared feed came out blurry — took nine exchanges to resolve, four of them crashes the assistant introduced itself, each costing a full round trip on a phone on a rural connection to report a typo. The fix that finally worked was simple and could have been the first attempt. Five patterns explain why it wasn't, and each is now a rule in the prompt, placed where the existing guidance already speaks. Edit blocks drift when they change a contract. All four crashes were the same failure: SEARCH/REPLACE hunks changed a function's return shape or introduced a module-level name, and a sibling hunk did not keep up — `small.fits` read by a caller the callee no longer served, a `THUMB_FROM` used in one hunk and defined in none. The "several distinct changes → multiple pairs in one block" rule is right for wording and local logic and actively dangerous for interface changes. Now: a change that alters a signature or return shape, or introduces a module-level name, outputs the complete file; and a reply with edit blocks confirms every identifier it introduces has exactly one definition in the same reply. Error paths are the one code path a build never runs. Three of the four crashes lived inside a "couldn't shrink this photo" message — a branch nothing in the build-and-preview cycle ever touches, so bugs there survive every check and surface on a neighbor's phone. Now: error, empty, and refusal branches carry no helpers and no values the working path doesn't already hold. Three confident theories, none verified. Three rounds each opened with a diagnosis stated as fact (Safari ignores the quality dial; sparse downsampling makes speckle; Safari again) and a fix built on it. None was testable — the assistant cannot run code on an iPhone. The fix that worked contained no theory: encode, measure, step down until it fits. A new section, "When the Bug Lives Somewhere You Cannot See": prefer code that measures and adapts over code that encodes a model of the device; after one failed fix on such behaviour, stop fixing and ship the smallest thing that reports what the device actually did, saying plainly that the round is for finding out; say which kind of round it is. Overconfident closing lines — the one the person named as worst. "It should just work." "A refusal is now essentially impossible." Four promises, all false, and each made the next failure a broken promise rather than a bug report. Now, in the chat-style rules: never predict the outcome of a change you could not run; say what changed, why it should address the cause, and what to look for, framed as a check; name untested assumptions and what would disprove them; and when a fix already failed, say so before proposing another. The gap under all of it is the platform's: Community Cloud has no blob storage, so every photo workaround in that session existed to squeeze an image through a 32KB JSON document — about 20KB of actual picture, a thumbnail. The remaining blur is that arithmetic, not a bug. Until an upload capability exists, the Community Cloud guidance now says the ceiling out loud before anyone builds on it, and gives the measure-don't-theorize shrink recipe so nobody derives it during a live debugging session. Prompt cost: the build prompt grows about 3.5K chars (~875 tokens, ~7%), all in the cached prefix; the photo bullet adds ~1.1K only when Community Cloud is connected. Verified the composed prompt carries every rule in the intended section order with zero page errors; tsc -b clean, eslint clean on both files. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RFnFvBZPsVQjc5Xwj7H9nZ
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Prompt-only. Two files, +13 lines, no code paths touched. Independent of #8, #10, #11.
The field report
Deb's Sahale Calendar (Kitsap Peninsula) has a feed where neighbors post what they notice on the land, often with a photo taken on the spot. Photos arrived blurry. A small, well-defined bug took nine exchanges, four of which were crashes the assistant introduced itself — each costing a full round trip, on a phone, on a rural connection, to report a typo. The fix that finally worked was simple and could have been the first attempt.
undefined is not an object ('small.fits')can't find variable: inKbcan't find variable: kbcan't find variable: thumb_fromFive patterns explain why. Each is now a rule, placed where the existing prompt already speaks rather than pasted on as a block.
What changed, and where
1. Edit blocks drift when they change a contract → Editing Existing Files. All four crashes were one failure: SEARCH/REPLACE hunks changed a function's return shape or introduced a module-level name, and a sibling hunk didn't keep up. The existing "several distinct changes → multiple pairs in one block" rule is right for wording and local logic, and actively dangerous for interface changes. Now: a change that alters a signature/return shape or introduces a module-level name outputs the complete file; and a reply with edit blocks confirms every identifier it introduces has exactly one definition in the same reply.
2. Error paths are the one code path a build never runs → Quality Rules. Three of the four crashes lived inside "couldn't shrink this photo" — a branch nothing in build-and-preview ever touches, so bugs there survive every check and surface on a neighbor's phone. Now: error, empty, and refusal branches carry no helpers and no values the working path doesn't already hold.
3 + 4. Three confident theories, none verified; fixes before diagnosis → new section, When the Bug Lives Somewhere You Cannot See. Rounds 2, 5, 7 each opened with a diagnosis stated as fact and a fix built on it; none was testable, because the assistant cannot run code on an iPhone. The fix that worked contained no theory. Now: prefer code that measures and adapts over code that encodes a model of the device; after one failed fix on device-side behaviour, stop fixing and ship the smallest thing that reports what the device actually did, saying plainly the round is for finding out; and say which kind of round it is.
5. Overconfident closing lines — the one Deb named as worst → Chat Style While Building. "It should just work." "A refusal is now essentially impossible." Four promises, all false, and each made the next failure a broken promise rather than a bug report. Now: never predict the outcome of a change you could not run. Say what changed, why it should address the cause, and what to look for — framed as a check, not a promise. Name untested assumptions and what would disprove them. When a fix already failed, say so before proposing another.
The gap underneath → Community Cloud guidance (
catalog.ts). Every workaround in that session existed to squeeze a photo through a 32KB JSON document. Base64 inflates by a third, so that's ~20KB of actual image — a thumbnail, not a photograph; Deb's own numbers (21–29KB at 320px) bear it out. The remaining blur she accepted is that arithmetic, not a bug. Until an upload capability exists, the guidance now says the ceiling out loud before anyone builds on it and gives the measure-don't-theorize shrink recipe, so nobody derives it during a live debugging session.Cost
The build prompt grows ~3.5K chars (~875 tokens, ~7%), all in the cached prefix. The photo bullet adds ~1.1K only when Community Cloud is connected. Verified the composed prompt carries every rule in the intended section order, zero page errors;
tsc -bclean,eslintclean on both files.Not in this PR — the real fix
Blob storage for Community Cloud (an upload alongside the JSON document store: upload returns a URL, documents reference it). Photos are among the most natural things neighbors contribute — a noticing on a trail, a found cat, a finished repair — and today every builder who wants them reinvents the same chunking scheme, badly, and ends with soft pictures. There's a seed already:
supabase/functions/story-photoshosts images on the Builder backend's public storage for commons stories, gated to signed-in builders. Anapp-filescapability for neighbor uploads would need its own abuse posture (per-app quotas, size caps, moderation in the Cloud tab), which is why it's a platform decision rather than a line in this PR. The prompt rule is written so it becomes simply wrong the day that ships — "there is no file or image upload yet" — and should be removed with it.Source: field report by Deb, Sahale Calendar, February 2026. Open items on her build (photos still softer than they should be; a
photopartscollection andsrc/lib/photos.tschunking that come out together if blob storage lands; untested on Android) belong to that project, not this repo.🤖 Generated with Claude Code
https://claude.ai/code/session_01RFnFvBZPsVQjc5Xwj7H9nZ
Generated by Claude Code