feat(distribution): mastodon + devto adapters + cross-post + mention monitor (v0.2)#2
Open
Wise-Est-Systems wants to merge 1 commit into
Conversation
…monitor Builds the outreach arm out from PR #1's two adapters to four, adds the two workflows that drive multi-channel publishing and incoming-mention monitoring. Same trust boundary — every send still passes through the existing approval gate. What's new ========== workflows/distribution/adapters/mastodon.py draft — LLM-generated toot under 500 chars; appends hashtags. submit — POST /api/v1/statuses with bearer token; returns toot id + URL. monitor — GET /api/v1/notifications, filters mentions to threads replying to the submitted status id. workflows/distribution/adapters/devto.py draft — LLM-generated long-form (400-1200 words) markdown post + canonical_url pointing at source repo (so dev.to does not outrank the canonical artifact in search). submit — POST /articles with API-Key header; published=true; up to 4 normalized tags. monitor — GET /comments?a_id=<id> for new comments. workflows/cross_post_pipeline.py Fan-out workflow. Takes one DistributionEvent and fires drafts across *every* READY adapter in parallel (vs distribution_pipeline which uses per-ask-type singleton defaults). One approval card per channel. Use case: release-day announcement to all channels at once. workflows/mention_monitor_pipeline.py Polls public search endpoints (hn.algolia.com, reddit.com/search.json, Mastodon /api/v2/search) for project keywords (wiseorder, winstack, WISEATA, wisedigest, .win file, wise-est by default). Creates one approval card per hit with URL + author + body preview. No auth needed for HN or Reddit; uses the Mastodon access token if available, falls back to public anonymous search otherwise. agents/outreach/drafter.py Added draft_mastodon_post() and draft_blog_post() methods. Same litellm.acompletion pattern as the existing draft_hn_post() and draft_email() — bounded retries, JSON-parsed output. configs/settings.py New env vars: WISEORDER_MASTODON_INSTANCE_URL / _ACCESS_TOKEN / _DEFAULT_VISIBILITY, WISEORDER_DEVTO_API_KEY. core/orchestrator/main.py Registers run_cross_post_pipeline and run_mention_monitor_pipeline as handlers. Registers MastodonAdapter and DevToAdapter alongside the existing two. Idempotent across construction. .env.distribution.example Documents the new env vars with comments on how to obtain each. Tests ===== tests/test_distribution_v0_2_smoke.py — 16 new pure tests covering: - Mastodon adapter: 500-char truncation, unconfigured-failure path, channel mismatch guard, HTML stripping - dev.to adapter: tag normalization rules, canonical_url propagation, unconfigured + missing-title failure paths - cross_post_pipeline: payload parsing, fan-out across READY adapters, target-list narrowing, degraded-adapter exclusion - mention_monitor: MentionHit serialization, approval-card construction, Algolia + Reddit JSON parsers (mocked httpx) make test-pure → 54 passed, 2 deselected, 1 warning in ~2s (38 of those are existing; 16 are new in this PR.) Orchestrator live load ====================== handlers: ['commit_pipeline', 'cross_post_pipeline', 'demo_request', 'distribution_pipeline', 'mention_monitor_pipeline'] distribution channels: ['devto', 'email_outreach', 'hacker_news', 'mastodon'] ready: ['devto', 'email_outreach', 'hacker_news', 'mastodon'] Stacked on PR #1 ================ This branch is stacked on feat/distribution-pipeline-v0.1 (PR #1). Merge PR #1 first; this PR's base auto-rebases to main when that happens. DESIGN-PENDING ============== 1. cross_post_pipeline's default fan-out goes to ALL READY adapters when target_channels is empty. That is the "release-day" shape. For non-release events you'd typically pass target_channels explicitly. Open to flipping the default if you want narrower auto-fan-out. 2. mention_monitor_pipeline's _DEFAULT_KEYWORDS list is hard-coded in the source. Could be moved to a settings field or a config file. 3. Mastodon's notification poll filters to in_reply_to == submission_id; it does NOT surface mentions of the submission via boost / favorite that did not generate a reply status. If you want those too, that's one more filter pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Stacks on PR #1. Builds the outreach arm out from PR #1's two adapters to four, adds the two workflows that drive multi-channel publishing and incoming-mention monitoring.
Same trust boundary as the rest of
wiseorder— every draft passes throughcore/approvals/gateway.pybefore any outbound side effect. Nothing autonomous reaches a public surface.What's in this PR
workflows/distribution/adapters/mastodon.pyPOST /api/v1/statuses) / monitor (GET /api/v1/notificationsfiltered to replies).workflows/distribution/adapters/devto.pyPOST /articles) / monitor (GET /comments).workflows/cross_post_pipeline.pyworkflows/mention_monitor_pipeline.py/api/v2/searchfor keywords. Creates an approval card per hit. No auth required for HN or Reddit.agents/outreach/drafter.pydraft_mastodon_post()anddraft_blog_post()methods, same litellm pattern as existing drafters.configs/settings.py+.env.distribution.exampleWISEORDER_MASTODON_INSTANCE_URL/_ACCESS_TOKEN/_DEFAULT_VISIBILITY,WISEORDER_DEVTO_API_KEY.core/orchestrator/main.pytests/test_distribution_v0_2_smoke.pyDESIGN-PENDING
Three places I picked a conservative default:
cross_post_pipelinedefault fan-out = ALL READY adapters whentarget_channelsis empty. That's the release-day shape. For non-release events you'd passtarget_channelsexplicitly. Open to flipping the default if you want narrower auto-fan-out.mention_monitor_pipeline._DEFAULT_KEYWORDSis hard-coded in source. Could be moved to a settings field or config file. Reasonable either way; flagged for your call.monitor()filters notifications toin_reply_to == submission_id. Does NOT surface boost/favorite-only mentions. If you want those too it's one more filter pass.Test evidence
Credential burden on you (one-time, ~30 minutes total)
To activate the new adapters:
Both go into
.env.distribution(gitignored). The two new pipelines work without these creds too — they just route to existing channels or skip the unconfigured ones.Stacked on PR #1
Base is
feat/distribution-pipeline-v0.1. Merge PR #1 first; this PR's base auto-rebases tomainwhen that happens.🤖 Generated with Claude Code