add: Long_Form_Strategy projector + fosse_long_form_strategy option#29
Draft
add: Long_Form_Strategy projector + fosse_long_form_strategy option#29
Conversation
Per the 2026-04-23 call with Jim Ray (Bluesky devrel), standard.site native rendering is S-tier on Bluesky's roadmap but months out and multi-iteration. That changes the trade-off from "short bridge with Option 2 until Option 3 lands" to "long bridge," making Option 5's upstream cost worth paying for v1. spec.md: - Flip Recommended v1 from Option 2 to Option 5. - Promote Option 5 analysis (SELECTED), downgrade Option 2 to opt-in. - Rewrite Technical Details around sequential-writes-with-rollback, ordered-array post meta, and filterable 2-post default composition. - Refresh Open Questions (composition details, 3-post variant, rollback observability) and add the Jim Ray call + "clearly better" criteria to Open Questions Resolved. plan.md (new): - Five tasks: one upstream Atmosphere PR (composition + Publisher redesign), bundle refresh, FOSSE projector, e2e thread capture, docs. - Each task has file-level precision and TDD-style commit granularity so a fresh agent can execute from the plan. Paired with DOTCOM-16810 (now Todo). Part of the DOTCOM-16795 epic.
Two review passes on the long-form Option 5 SDD (plan-executability and AT-Protocol-correctness) surfaced a handful of real issues. This commit folds in the confident fixes; remaining decisions are flagged for confirmation on PR #24. spec.md: - Reframe atomicity: the constraint isn't "client-side CID computation required" — it's "reply records need the parent's CID which only arrives via server response." Two-call batching isn't possible for a chained thread because each post's parent CID is unknown until commit. - Architecture/diagram: rename Publisher's composition entry point to build_long_form_records() and make the short/long branch explicit. - Thread write semantics: partial-meta writes after each successful create (crash-recovery guard); createdAt stamped at write time per post (not pre-computed); langs inherited consistently across the thread; facet byte-offsets computed against each post's own text. - 3-post thread refs clarified: reply.parent chains (post 3 parents post 2, not root). - Post meta shape: single array of {uri, cid, tid} triples under _atmosphere_bsky_thread_records, not parallel arrays. Single-value legacy keys preserved as root mirrors. - Publisher::update() limitations documented: orphans replies from other users; resets in-feed timestamp. Both go in the readme.txt changelog. - File Changes: one upstream PR (not two); drop class-post-meta.php; capture helper switches from transition_post_status to pre_http_request; add AGENTS.md work. - Open Questions pared to the two actually-open items (3-post default composition; atomic-write upgrade path); everything else moved to Resolved with "applied (confirm)" markers so Kraft can redirect from PR #24 without rewriting the plan. plan.md (full rewrite): - Task 1 opens as a draft PR first so upstream review runs in parallel. - Commit-level TDD structure preserved. - New method names (build_long_form_records / build_teaser_thread / build_truncate_link_text / truncate_at_word_boundary) with exact signatures. - Publisher tasks explicitly address store_results() and update_document_bsky_ref() helpers. - Task 3 (projector) coerces unset/unknown to 'teaser-thread' — opinionated default, documented divergence from Object_Type's pass-through. - Task 4 (e2e) rewrites the capture mu-plugin as a pre_http_request interceptor; existing specs get shape updates. - File-placement guidance fixed (META_THREAD_RECORDS lives on Post); fosse.php wiring uses the existing anonymous-function + class_exists pattern; CHANGELOG.md reference dropped (FOSSE uses readme.txt).
Per Kraft's 2026-04-23 review of the pickup comment: - The final prose cut before the CTA (v1's post 1 hook) clamps at a sentence boundary — last `.`/`!`/`?` (with optional trailing close- quote/bracket) ≤ 280 graphemes. Word boundary is the fallback only when no sentence break exists in the window; mid-word is a last resort for single very long words. - In a future 3+-post variant, intermediate body-to-body cuts can be word-boundary (mid-word permitted); the sentence-boundary rule only applies to the last body post before the CTA. - Upstream `atmosphere_long_form_composition` default remains 'link-card'. The teaser-thread opinion lives entirely in FOSSE's projector; if the Atmosphere plugin team wants to change the upstream default later, that's their call to make separately. Spec: - Composition (2-post default) and Option 5's composition narrative now specify sentence-first with word fallback. - Key Components table renames the helper to `truncate_to_budget(text, max, prefer_sentence = true)` — sentence → word → hard-cap. - Open Questions Resolved gets the 2026-04-23 calls explicitly. plan.md: - Task 1 Commit 1: `truncate_to_budget()` signature + implementation sketch with the `$prefer_sentence` parameter and fallback chain. - `build_truncate_link_text()` uses `$prefer_sentence = false` (word boundary is enough for a single-post strategy where the permalink follows immediately). - `build_teaser_thread()` uses `$prefer_sentence = true` for the hook and notes the 3-post variant's boundary rule as a filter-author responsibility. - Task 1 Commit 2: tests split to cover sentence-first, trailing-close- punctuation, no-sentence fallback, word-only mode, hard-cap. - Task 4 e2e: assert the hook ends at sentence-closing punctuation.
Kraft's 2026-04-23 follow-up on the PR #24 pickup comment: - Excerpts (`$post->post_excerpt`) win as the hook source when set. User-curated copy beats a machine body-prefix truncation. Body path stays as the fallback, with sentence-boundary clamping. - Empty / whitespace-only body AND no excerpt → degrade to link-card composition for that post only (site option unchanged); emit a notice/log so ops can tell the fallback from an intentional link-card configuration. Threshold: < 10 graphemes of rendered plain text. - `atmosphere_transform_bsky_post` fires per thread record, not once per WP post. Consistent treatment; behavior change documented in changelog. No auto-elision. spec.md: - Composition (2-post default) captures excerpt precedence + empty- body fallback + per-record filter semantics. - Option 5 composition block tightens the hook precedence order. - Open Questions Resolved gets the three new decisions with their resolver attribution. plan.md: - Task 1 Commit 1 step 4 (`build_teaser_thread()`) now reads the excerpt first; step 5 (`build_long_form_records()`) has the empty- body degradation guard and documents the per-record filter change. - Task 1 Commit 2 tests add excerpt-as-hook and empty-body-degrades- to-link-card cases.
Base automatically changed from
sdd-long-form-bluesky-strategy-DOTCOM-16810
to
trunk
April 24, 2026 18:00
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.
Stacked on #24 — this is Task 3 of the long-form Bluesky strategy SDD.
Fixes DOTCOM-16887
See DOTCOM-16810 (parent epic)
What this adds
Forward-compatible, no-op until upstream lands
The filter the projector hooks (`atmosphere_long_form_composition`) is added by Automattic/wordpress-atmosphere#34 — still draft. Until that merges and Task 2 refreshes `bundled/atmosphere/`, nothing in the current bundled copy ever fires the filter, so this projector registers a callback no one calls. Harmless. When upstream lands, this just works — no FOSSE change needed beyond the `tools/sync-bundled.sh` refresh.
Why stack on #24
Staying as a draft
Per session convention — no reviewers pinged on this or the parent PRs.