| … | … | @@ -2608,8 +2608,31 @@ const server = http.createServer(async (req, res) => { |
| 2608 | 2608 | if (pg) await writer.putRecord('app.bsky.feed.postgate', out.rkey, pg, { action: 'create' }) |
| 2609 | 2609 | } catch (e) { console.error(JSON.stringify({ msg: 'postgate write failed (post itself is fine)', error: e.message })) } |
| 2610 | 2610 | } |
| 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 {} |
| 2613 | 2636 | // ?posted=1 is the SUCCESS signal draft.js waits for. Clearing the draft on submit instead would |
| 2614 | 2637 | // throw the text away in exactly the case draft recovery exists for — the write that failed. |
| 2615 | 2638 | return isJson ? json(res, 200, JSON.stringify(out)) : redirect(res, backTo + (backTo.includes('?') ? '&' : '?') + 'posted=1') |