OverviewFilesCommitsIssuesDocsReleasesPeers

favela-web commits / 3c2a624

index our own post immediately, not just our own reply
A reply appeared in its thread the instant it was written; a top-level post did not appear on the timeline until the firehose came back around. Same handler, one `if (reply)` apart. That reads as "my post vanished", and today it sent us looking for a sync bug that was this asymmetry. Also stop rebuilding the record. The hand-built shape carried text/createdAt/ reply and dropped langs, embeds and facets, so the optimistic row was a DEGRADED COPY that then changed under the reader when the real one landed — an image appearing seconds later, a link becoming a link. The bytes hpp just signed are the truth, so read them back; the rebuild stays only as a fallback for the window where the write is durable but not yet readable. Safe to run twice: putPost's dedup gate gets the same recSig, so the fold that follows re-reads without re-writing. ⚠ NOT COVERED BY A TEST. All 44 suites target lib/; bin/favela-web.js has no harness, and building one for a 3000-line route file is not this change. The call is the one already proven on the reply path. Verifiable on a live node: post, and the row is there on the redirect. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3c2a6240e697f66d7ae410f3e869cb883806d074
by plankatron <fleeky@gmail.com> · 2026-08-18T14:18:35.000Z

Changed files (1) +25 -2

bin/favela-web.js +25 -2
……@@ -2608,8 +2608,31 @@ const server = http.createServer(async (req, res) => {
26082608 if (pg) await writer.putRecord('app.bsky.feed.postgate', out.rkey, pg, { action: 'create' })
26092609 } catch (e) { console.error(JSON.stringify({ msg: 'postgate write failed (post itself is fine)', error: e.message })) }
26102610 }
2611 // Index our own reply immediately so the thread shows it without waiting for the firehose round-trip.
2612 if (reply) { try { await av.putPost({ did: (await writer.state()).did, collection: out.collection, rkey: out.rkey, record: { $type: out.collection, text, createdAt: new Date().toISOString(), reply } }) } catch {} }
2611 // Index our own write immediately so it shows without waiting for the firehose round-trip.
2612 //
2613 // ⛔ THIS USED TO BE `if (reply)`. A reply appeared in its thread instantly and a top-level post did
2614 // not appear on the timeline at all until the firehose came back around — which reads as "my post
2615 // vanished", and on 2026-08-18 sent us looking for a sync bug that was this asymmetry.
2616 //
2617 // READ THE RECORD BACK rather than rebuilding it. The hand-built shape carried text/createdAt/reply
2618 // and dropped langs, embeds and facets, so an optimistic row rendered as a DEGRADED COPY of the post
2619 // that then changed under the reader when the real one landed — an image appearing seconds later, a
2620 // link becoming a link. The bytes hpp just signed are the truth; ask for them. The rebuild stays as
2621 // a fallback for the window where the write is durable but not yet readable.
2622 //
2623 // Safe to run twice: putPost is dedup-gated by content signature, so the firehose fold that follows
2624 // re-reads without re-writing.
2625 try {
2626 const wdid = (await writer.state()).did
2627 let rec = null
2628 try { rec = await (await getRecordStore(wdid)).get(out.collection, out.rkey) } catch {}
2629 await av.putPost({
2630 did: wdid,
2631 collection: out.collection,
2632 rkey: out.rkey,
2633 record: rec || { $type: out.collection, text, createdAt: new Date().toISOString(), langs, ...(reply ? { reply } : {}) }
2634 })
2635 } catch {}
26132636 // ?posted=1 is the SUCCESS signal draft.js waits for. Clearing the draft on submit instead would
26142637 // throw the text away in exactly the case draft recovery exists for — the write that failed.
26152638 return isJson ? json(res, 200, JSON.stringify(out)) : redirect(res, backTo + (backTo.includes('?') ? '&' : '?') + 'posted=1')