Skip to content

feat(distribution): mastodon + devto adapters + cross-post + mention monitor (v0.2)#2

Open
Wise-Est-Systems wants to merge 1 commit into
feat/distribution-pipeline-v0.1from
feat/distribution-pipeline-v0.2
Open

feat(distribution): mastodon + devto adapters + cross-post + mention monitor (v0.2)#2
Wise-Est-Systems wants to merge 1 commit into
feat/distribution-pipeline-v0.1from
feat/distribution-pipeline-v0.2

Conversation

@Wise-Est-Systems

Copy link
Copy Markdown
Owner

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 through core/approvals/gateway.py before any outbound side effect. Nothing autonomous reaches a public surface.

What's in this PR

Area Added
workflows/distribution/adapters/mastodon.py Full adapter: draft (≤500 chars) / submit (POST /api/v1/statuses) / monitor (GET /api/v1/notifications filtered to replies).
workflows/distribution/adapters/devto.py Full adapter: draft (long-form markdown + canonical_url so dev.to does not outrank source) / submit (POST /articles) / monitor (GET /comments).
workflows/cross_post_pipeline.py Fan-out workflow. Takes one event → drafts across every READY adapter → one approval card per channel. Use case: release-day announcement.
workflows/mention_monitor_pipeline.py Polls HN Algolia + Reddit JSON search + Mastodon /api/v2/search for keywords. Creates an approval card per hit. No auth required for HN or Reddit.
agents/outreach/drafter.py Adds draft_mastodon_post() and draft_blog_post() methods, same litellm pattern as existing drafters.
configs/settings.py + .env.distribution.example New env vars: WISEORDER_MASTODON_INSTANCE_URL / _ACCESS_TOKEN / _DEFAULT_VISIBILITY, WISEORDER_DEVTO_API_KEY.
core/orchestrator/main.py Registers two new pipeline handlers + two new adapters. Idempotent.
tests/test_distribution_v0_2_smoke.py 16 new pure tests; passes `make test-pure` with no Docker.

DESIGN-PENDING

Three places I picked a conservative default:

  1. cross_post_pipeline default fan-out = ALL READY adapters when target_channels is empty. That's the release-day shape. For non-release events you'd pass target_channels explicitly. Open to flipping the default if you want narrower auto-fan-out.
  2. mention_monitor_pipeline._DEFAULT_KEYWORDS is hard-coded in source. Could be moved to a settings field or config file. Reasonable either way; flagged for your call.
  3. Mastodon monitor() filters notifications to in_reply_to == submission_id. Does NOT surface boost/favorite-only mentions. If you want those too it's one more filter pass.

Test evidence

make test-pure  →  54 passed, 2 deselected, 1 warning in ~2s
                   (16 of those 54 are new in this PR; 38 are existing from PR #1 + smoke + hardening)

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']

Credential burden on you (one-time, ~30 minutes total)

To activate the new adapters:

  • Mastodon: 5 min — create an account on any instance, generate access token under Preferences → Development → New application
  • dev.to: 5 min — generate an API key at dev.to/settings/extensions

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 to main when that happens.

🤖 Generated with Claude Code

…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant