Skip to content

Add chappe wrapped: shareable PNG dashboard with growth-loop attribution#2

Merged
crimeacs merged 3 commits into
mainfrom
feature/wrapped-dashboard
May 19, 2026
Merged

Add chappe wrapped: shareable PNG dashboard with growth-loop attribution#2
crimeacs merged 3 commits into
mainfrom
feature/wrapped-dashboard

Conversation

@crimeacs
Copy link
Copy Markdown
Owner

Summary

  • New top-level command chappe wrapped @channel --period 90d [--lang en|ru] [--out PATH] that renders a 1080×1350 PNG dashboard for the channel plus a ready caption template.
  • The PNG carries 'generated by chappe · github.com/crimeacs/chappe' in the footer, so every operator who posts their wrapped image is also doing a quiet referral.
  • Wired into onboarding so the first reachable payoff after sync is something the operator can publish to their own channel today, not just JSON.

Why

Today the first real payoff after onboarding is JSON in a terminal. That's good for agents, but it's not something the operator can immediately turn into a post. chappe wrapped closes that gap: 1 command after sync → a finished image + finished caption, ready to publish. Three things compound on that:

  1. The operator gets a free, on-topic engagement post for their own channel ('here's what my channel looks like, courtesy of Chappe').
  2. The footer references the repo, so curious followers get a clean install path.
  3. Chappe's onboarding now ends with a deliverable, not a docs link.

Visual

1080×1350 vertical card, dark theme. Header (channel + period) → 3 big stat tiles (posts, comments, mean fwd/views) → top 3 posts by forwards with id, views, forwards → best-format + sample audience question → footer with Chappe attribution and the repo URL. Cyrillic renders correctly via Arial / DejaVu fallback.

Onboarding wire-up

  • bootstrap fastest_path_to_value now surfaces a render_wrapped_dashboard step once the channel has ≥20 local posts. The why field is explicit about the growth loop ('free engagement post that quietly credits Chappe').
  • onboard setup_steps.analyze_growth is followed by publish_wrapped_dashboard.

Output shape

{
  "ok": true,
  "channel": "@nn_for_science",
  "period": "90d",
  "lang": "ru",
  "png_path": "/path/to/nn_for_science-90d.png",
  "caption_path": "/path/to/nn_for_science-90d.txt",
  "caption_preview": "📊 chappe-wrapped: @nn_for_science · 90d ...",
  "stats": { "posts": 48, "comments": 921, "mean_forward_rate": 0.01464, "best_format": "photo", "top_posts": [...], "sample_question": "..." },
  "next_commands": [ "chappe draft create @nn_for_science --file <caption.txt>", ... ],
  "growth_hint": "Post this PNG + caption to your own channel..."
}

Test plan

  • pytest -q — 41 passed (3 new for wrapped + the existing 38)
  • ruff check src/ tests/ — clean
  • Smoke-tested live on @nn_for_science: PNG renders, Cyrillic renders, footer attribution lands, caption .txt file is written next to the PNG
  • wrapped on an unsynced channel fails with NOT_FOUND and an explicit chappe sync <ch> next_command
  • bootstrap --channel @ch after 20 stored posts surfaces the new render_wrapped_dashboard step in fastest_path_to_value

Known limitation

Pillow does not render color emoji glyphs. A '🤖' inside a post's text shows as a placeholder square. Acceptable for v1; can ship an emoji-stripping pre-pass in a follow-up if it becomes a real complaint.

🤖 Generated with Claude Code

crimeacs added 3 commits May 19, 2026 14:04
`chappe wrapped @channel --period 90d [--lang en|ru] [--out PATH]` reads the
local store, computes channel stats (posts, comments, mean forward-rate, best
format, top 3 by forwards, sample audience question), and writes a 1080x1350
PNG card plus a ready caption .txt. The PNG carries 'generated by chappe ·
github.com/crimeacs/chappe' in the footer, so each post published from it is a
quiet referral.

Wired into onboarding:

- bootstrap fastest_path_to_value now suggests `chappe wrapped` once a channel
  has ≥20 local posts. The 'why' explicitly frames it as a free engagement post.
- onboard setup_steps appends a publish_wrapped_dashboard step at the end of
  analyze_growth.

This makes the first reachable payoff after a fresh sync a tangible image the
operator can post immediately, not just JSON. The Chappe attribution turns each
adopter's first post into a growth loop.

- new dashboard.py: pure rendering (Pillow) + compute_dashboard_stats + bilingual
  render_caption; tolerates missing Cyrillic glyphs by falling back through a
  font-path candidate list (Arial → DejaVu)
- new pyproject dep: Pillow>=10
- new cli command `wrapped` and two new onboarding steps
- 3 new tests: png magic bytes + caption file + sync next_command on unsynced +
  bootstrap surface for the new fastest-path step
The first cut was a generic dark stats card. This rewrites the renderer as a
vintage-dispatch poster that pairs the Chappie mascot with the channel's
strongest post as a pull-quote, sits on cream paper with deterministic noise,
and prints in a strict 3-ink palette (forest green, sepia, cream).

Visual moves
- Mascot is the hero. _load_mascot_keyed() alpha-keys the near-white sky in
  the source illustration so Chappie sits directly on the paper instead of
  inside a hard rectangle.
- Mascot rotates per channel via deterministic seed (channel handle hash),
  picking from chappie-recorder, signal-operator, lookout, night-watch, or
  scout-map-reader. Same channel always renders the same mascot.
- Channel name in Baskerville Bold (true upright weight via display_bold
  font role), not Hoefler Black Italic which is what serif_black resolves to
  on macOS.
- Pull-quote from the #1 post by forwards in serif Regular, with a faint
  oversized opening quote ornament so the quote teases without competing
  with the masthead.
- Two feature stats below the quote: top-post forwards and the channel's
  mean forward rate.
- Bottom ledger is four numeric cells with a consistent rhythm (posts,
  comments, questions, peak views) plus thin vertical separators.
- Footer attribution + monospaced URL.

Stats fixes
- _best_question() now ranks comment questions by reactions then length,
  filtering out trivially short ones. Lands on substantive questions
  instead of the first ?-ending fragment in the corpus.
- compute_dashboard_stats() distinguishes most_used_format (count) from
  best_format_by_lift (forward rate). The visual no longer surfaces the
  bare 'photo' label; the caption shows both axes.
- New peak_views and peak_forwards rollups.

Assets shipped in src/chappe/assets/ so they ride along in the wheel:
chappie-recorder.png, chappie-signal-operator.png, chappie-lookout.png,
chappie-night-watch.png, chappie-scout-map-reader.png.
@crimeacs crimeacs merged commit 387391d into main May 19, 2026
4 checks passed
@crimeacs crimeacs deleted the feature/wrapped-dashboard branch May 19, 2026 18:32
crimeacs added a commit that referenced this pull request May 19, 2026
User-facing additions since 0.1.2:

- chappe compare @ch1 @ch2 [...]: cross-channel leaderboard with mean
  forward-rate leader and raw-metric winner. PR #1.
- chappe wrapped @channel: PNG channel-dashboard renderer plus caption
  template with Chappe footer attribution; mascot rotates per channel and
  Pillow renders cream-paper letterpress style. Onboarding now surfaces it
  as the first reachable payoff once a channel has ≥20 stored posts. PR #2.
- New Pillow>=10 dependency.
- Bundled five Chappie mascot illustrations inside the wheel.
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