luckiest-co 1.0.47 → 1.0.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/skills/ab-testing/CHANGELOG.md +5 -0
- package/skills/ab-testing/SKILL.md +12 -9
- package/skills/ab-testing/evals/evals.json +18 -7
- package/skills/ab-testing/references/sample-size-guide.md +2 -2
- package/skills/ads/CHANGELOG.md +5 -0
- package/skills/ads/SKILL.md +4 -4
- package/skills/ai-seo/CHANGELOG.md +7 -0
- package/skills/ai-seo/SKILL.md +27 -20
- package/skills/analytics/CHANGELOG.md +6 -0
- package/skills/analytics/SKILL.md +4 -3
- package/skills/analytics/evals/evals.json +27 -0
- package/skills/analytics/references/ga4-implementation.md +7 -3
- package/skills/analytics/references/gtm-implementation.md +8 -2
- package/skills/aso/CHANGELOG.md +5 -0
- package/skills/aso/SKILL.md +4 -4
- package/skills/aso/evals/evals.json +15 -2
- package/skills/aso/references/benchmarks.md +4 -2
- package/skills/aso/references/google-play-specs.md +5 -1
- package/skills/brainstorm/SKILL.md +59 -16
- package/skills/brainstorm/builder-check-prompt.md +23 -0
- package/skills/churn-prevention/CHANGELOG.md +5 -0
- package/skills/churn-prevention/SKILL.md +3 -3
- package/skills/churn-prevention/evals/evals.json +14 -0
- package/skills/churn-prevention/references/dunning-playbook.md +6 -1
- package/skills/coder-brainstorming/CHANGELOG.md +10 -0
- package/skills/coder-brainstorming/SKILL.md +62 -20
- package/skills/coder-brainstorming/builder-check-prompt.md +23 -0
- package/skills/coder-brainstorming/scripts/server.cjs +1 -1
- package/skills/coder-debugging/CHANGELOG.md +4 -0
- package/skills/coder-debugging/SKILL.md +5 -5
- package/skills/coder-requesting-code-review/CHANGELOG.md +4 -0
- package/skills/coder-requesting-code-review/SKILL.md +4 -4
- package/skills/coder-subagent-driven-development/CHANGELOG.md +4 -0
- package/skills/coder-subagent-driven-development/SKILL.md +3 -3
- package/skills/coder-subagent-driven-development/scripts/sdd-workspace +3 -1
- package/skills/cold-email/CHANGELOG.md +7 -0
- package/skills/cold-email/SKILL.md +27 -7
- package/skills/cold-email/evals/evals.json +30 -0
- package/skills/cold-email/references/benchmarks.md +15 -11
- package/skills/cold-email/references/deliverability.md +110 -0
- package/skills/cold-email/references/linkedin-outreach.md +91 -0
- package/skills/cold-email/references/multichannel-cadence.md +79 -0
- package/skills/cold-email/references/outbound-plays.md +88 -0
- package/skills/cold-email/references/personalization.md +11 -0
- package/skills/cold-email/references/reply-handling.md +65 -0
- package/skills/competitor-profiling/CHANGELOG.md +5 -0
- package/skills/competitor-profiling/SKILL.md +4 -4
- package/skills/content-strategy/CHANGELOG.md +5 -0
- package/skills/content-strategy/SKILL.md +5 -3
- package/skills/copy-editing/CHANGELOG.md +5 -0
- package/skills/copy-editing/SKILL.md +5 -5
- package/skills/copy-editing/references/content-refresh.md +33 -0
- package/skills/cro/CHANGELOG.md +5 -0
- package/skills/cro/SKILL.md +14 -3
- package/skills/cro/references/form.md +1 -1
- package/skills/directory-submissions/CHANGELOG.md +5 -0
- package/skills/directory-submissions/SKILL.md +13 -4
- package/skills/directory-submissions/evals/evals.json +15 -1
- package/skills/directory-submissions/references/directory-list.md +2 -4
- package/skills/emails/CHANGELOG.md +5 -0
- package/skills/emails/SKILL.md +14 -4
- package/skills/launch/CHANGELOG.md +7 -0
- package/skills/launch/SKILL.md +8 -4
- package/skills/launch/evals/evals.json +29 -0
- package/skills/launch/references/shipped-changes.md +84 -0
- package/skills/launch/references/site-launch-qa.md +55 -0
- package/skills/lead-magnets/CHANGELOG.md +5 -0
- package/skills/lead-magnets/SKILL.md +6 -4
- package/skills/lead-magnets/evals/evals.json +18 -0
- package/skills/lead-magnets/references/content-to-skill-corpus-design.md +89 -0
- package/skills/lead-magnets/references/content-to-skill-release.md +71 -0
- package/skills/lead-magnets/references/content-to-skill.md +108 -0
- package/skills/marketing-plan/CHANGELOG.md +5 -0
- package/skills/marketing-plan/SKILL.md +3 -3
- package/skills/marketing-plan/evals/evals.json +13 -0
- package/skills/marketing-plan/references/budget-planning.md +26 -17
- package/skills/model-router/CHANGELOG.md +6 -0
- package/skills/model-router/SKILL.md +3 -3
- package/skills/model-router/scripts/route.py +1 -1
- package/skills/model-router/scripts/test_route.py +5 -0
- package/skills/pricing/CHANGELOG.md +5 -0
- package/skills/pricing/SKILL.md +4 -4
- package/skills/pricing/evals/evals.json +12 -0
- package/skills/pricing/references/research-methods.md +8 -4
- package/skills/programmatic-seo/CHANGELOG.md +5 -0
- package/skills/programmatic-seo/SKILL.md +8 -4
- package/skills/programmatic-seo/evals/evals.json +13 -0
- package/skills/programmatic-seo/references/implementation-platforms.md +37 -0
- package/skills/prospecting/CHANGELOG.md +7 -0
- package/skills/prospecting/SKILL.md +27 -13
- package/skills/prospecting/evals/evals.json +41 -0
- package/skills/prospecting/references/account-research.md +59 -0
- package/skills/prospecting/references/b2b-prospecting.md +1 -1
- package/skills/prospecting/references/compliance.md +44 -6
- package/skills/prospecting/references/data-sources.md +13 -15
- package/skills/prospecting/references/enrichment-playbook.md +61 -0
- package/skills/prospecting/references/saas-prospecting.md +1 -1
- package/skills/prospecting/references/signal-plays.md +99 -0
- package/skills/prospecting/references/sourcing-and-enrichment.md +89 -0
- package/skills/referrals/CHANGELOG.md +5 -0
- package/skills/referrals/SKILL.md +10 -4
- package/skills/referrals/evals/evals.json +99 -0
- package/skills/referrals/references/affiliate-operations.md +64 -0
- package/skills/referrals/references/affiliate-programs.md +2 -2
- package/skills/referrals/references/program-examples.md +14 -6
- package/skills/referrals/references/review-platform-rules.md +36 -0
- package/skills/referrals/references/review-program-operations.md +62 -0
- package/skills/referrals/references/review-programs.md +96 -0
- package/skills/revops/CHANGELOG.md +5 -0
- package/skills/revops/SKILL.md +10 -6
- package/skills/revops/evals/evals.json +13 -0
- package/skills/revops/references/automation-playbooks.md +1 -1
- package/skills/revops/references/inbound-routing.md +43 -0
- package/skills/revops/references/lifecycle-definitions.md +31 -0
- package/skills/sales-enablement/CHANGELOG.md +5 -0
- package/skills/sales-enablement/SKILL.md +6 -4
- package/skills/sales-enablement/evals/evals.json +14 -0
- package/skills/sales-enablement/references/cold-call-scripts.md +68 -0
- package/skills/schema/CHANGELOG.md +5 -0
- package/skills/schema/SKILL.md +20 -8
- package/skills/schema/evals/evals.json +40 -0
- package/skills/schema/references/schema-examples.md +9 -15
- package/skills/seo-audit/CHANGELOG.md +5 -0
- package/skills/seo-audit/SKILL.md +10 -16
- package/skills/seo-audit/evals/evals.json +68 -0
- package/skills/seo-audit/references/international-seo.md +9 -11
- package/skills/seo-audit/references/local-seo-pages-and-measurement.md +39 -0
- package/skills/seo-audit/references/local-seo-profile-and-citations.md +41 -0
- package/skills/seo-audit/references/local-seo.md +86 -0
- package/skills/seo-audit/references/rankings-push.md +62 -0
- package/skills/seo-audit/references/title-tags.md +89 -0
- package/skills/signup/CHANGELOG.md +5 -0
- package/skills/signup/SKILL.md +7 -3
- package/skills/site-architecture/CHANGELOG.md +5 -0
- package/skills/site-architecture/SKILL.md +5 -4
- package/skills/site-architecture/evals/evals.json +25 -0
- package/skills/site-architecture/references/platforms-and-migration.md +84 -0
- package/skills/social/CHANGELOG.md +5 -0
- package/skills/social/SKILL.md +4 -4
- package/skills/trends/.skillignore +3 -0
- package/skills/trends/CHANGELOG.md +20 -0
- package/skills/trends/SKILL.md +12 -8
- package/skills/trends/references/agent-mode.md +36 -0
- package/skills/trends/references/category-peers.md +38 -0
- package/skills/trends/references/comparison.md +227 -0
- package/skills/trends/references/competitors.md +29 -0
- package/skills/trends/references/discovery.md +90 -0
- package/skills/trends/references/engine-contract.md +111 -1889
- package/skills/trends/references/followups.md +112 -0
- package/skills/trends/references/grok-bot-x.md +55 -0
- package/skills/trends/references/hiring-signals.md +19 -0
- package/skills/trends/references/library-queue.md +33 -0
- package/skills/trends/references/recommendations.md +86 -0
- package/skills/trends/references/research-runbook.md +781 -0
- package/skills/trends/references/runtime.md +165 -0
- package/skills/trends/references/save-html-brief.md +39 -20
- package/skills/trends/references/setup-wizard.md +253 -0
- package/skills/trends/references/synthesis.md +289 -0
- package/skills/trends/scripts/box_chrome_login.py +209 -0
- package/skills/trends/scripts/briefing.py +9 -17
- package/skills/trends/scripts/last30days.py +3247 -280
- package/skills/trends/scripts/lib/agentcookie.py +196 -0
- package/skills/trends/scripts/lib/amazon.py +877 -0
- package/skills/trends/scripts/lib/arxiv.py +42 -8
- package/skills/trends/scripts/lib/backends.py +769 -0
- package/skills/trends/scripts/lib/bird_x.py +340 -57
- package/skills/trends/scripts/lib/bluesky.py +84 -10
- package/skills/trends/scripts/lib/bounded_get.py +156 -0
- package/skills/trends/scripts/lib/brightdata.py +291 -0
- package/skills/trends/scripts/lib/categories.py +7 -1
- package/skills/trends/scripts/lib/chrome_cdp.py +455 -0
- package/skills/trends/scripts/lib/chrome_cookies.py +132 -39
- package/skills/trends/scripts/lib/cluster.py +31 -54
- package/skills/trends/scripts/lib/competitors.py +10 -1
- package/skills/trends/scripts/lib/cookie_extract.py +221 -44
- package/skills/trends/scripts/lib/cookie_paths.py +27 -0
- package/skills/trends/scripts/lib/corpus.py +439 -0
- package/skills/trends/scripts/lib/devto.py +313 -0
- package/skills/trends/scripts/lib/discovery_handoff.py +988 -0
- package/skills/trends/scripts/lib/doctor.py +2166 -0
- package/skills/trends/scripts/lib/dripstack.py +203 -0
- package/skills/trends/scripts/lib/entity_extract.py +37 -0
- package/skills/trends/scripts/lib/env.py +1092 -138
- package/skills/trends/scripts/lib/fanout.py +11 -1
- package/skills/trends/scripts/lib/feed.py +61 -0
- package/skills/trends/scripts/lib/fetch_memo.py +135 -0
- package/skills/trends/scripts/lib/freshness.py +566 -0
- package/skills/trends/scripts/lib/fusion.py +314 -28
- package/skills/trends/scripts/lib/github.py +438 -24
- package/skills/trends/scripts/lib/grok_x.py +1237 -0
- package/skills/trends/scripts/lib/grounding.py +129 -11
- package/skills/trends/scripts/lib/hackernews.py +61 -4
- package/skills/trends/scripts/lib/health.py +373 -0
- package/skills/trends/scripts/lib/hosted.py +333 -0
- package/skills/trends/scripts/lib/html_publish.py +34 -0
- package/skills/trends/scripts/lib/html_render.py +169 -3
- package/skills/trends/scripts/lib/http.py +1215 -70
- package/skills/trends/scripts/lib/instagram.py +132 -9
- package/skills/trends/scripts/lib/jobs.py +4 -2
- package/skills/trends/scripts/lib/library.py +308 -0
- package/skills/trends/scripts/lib/library_index.py +477 -0
- package/skills/trends/scripts/lib/linkedin.py +271 -24
- package/skills/trends/scripts/lib/log.py +5 -2
- package/skills/trends/scripts/lib/meta_ads.py +982 -0
- package/skills/trends/scripts/lib/normalize.py +382 -28
- package/skills/trends/scripts/lib/parallel_mcp.py +234 -0
- package/skills/trends/scripts/lib/permission_preflight.py +42 -3
- package/skills/trends/scripts/lib/perplexity.py +1080 -468
- package/skills/trends/scripts/lib/pipeline.py +5079 -211
- package/skills/trends/scripts/lib/planner.py +396 -53
- package/skills/trends/scripts/lib/polymarket.py +285 -23
- package/skills/trends/scripts/lib/prescriptions.py +432 -0
- package/skills/trends/scripts/lib/private_files.py +21 -0
- package/skills/trends/scripts/lib/providers.py +231 -10
- package/skills/trends/scripts/lib/quality_nudge.py +122 -37
- package/skills/trends/scripts/lib/reddit.py +177 -12
- package/skills/trends/scripts/lib/reddit_arctic.py +146 -5
- package/skills/trends/scripts/lib/reddit_keyless.py +121 -60
- package/skills/trends/scripts/lib/reddit_listing.py +215 -14
- package/skills/trends/scripts/lib/reddit_public.py +12 -13
- package/skills/trends/scripts/lib/reddit_search.py +304 -0
- package/skills/trends/scripts/lib/reddit_shreddit.py +51 -7
- package/skills/trends/scripts/lib/registers.py +134 -0
- package/skills/trends/scripts/lib/relevance.py +17 -0
- package/skills/trends/scripts/lib/render.py +2161 -271
- package/skills/trends/scripts/lib/rerank.py +623 -55
- package/skills/trends/scripts/lib/resolve.py +50 -1
- package/skills/trends/scripts/lib/safari_cookies.py +42 -15
- package/skills/trends/scripts/lib/schema.py +782 -1
- package/skills/trends/scripts/lib/setup_wizard.py +779 -120
- package/skills/trends/scripts/lib/signals.py +206 -7
- package/skills/trends/scripts/lib/stocktwits.py +374 -0
- package/skills/trends/scripts/lib/subproc.py +307 -53
- package/skills/trends/scripts/lib/techmeme.py +89 -64
- package/skills/trends/scripts/lib/telegram.py +317 -0
- package/skills/trends/scripts/lib/tiktok.py +13 -9
- package/skills/trends/scripts/lib/topic_shape.py +340 -0
- package/skills/trends/scripts/lib/transcribe.py +12 -3
- package/skills/trends/scripts/lib/trustpilot.py +256 -5
- package/skills/trends/scripts/lib/truthsocial.py +9 -1
- package/skills/trends/scripts/lib/ui.py +6 -0
- package/skills/trends/scripts/lib/usage.py +132 -0
- package/skills/trends/scripts/lib/web_search_keyless.py +63 -4
- package/skills/trends/scripts/lib/windows_job.py +138 -0
- package/skills/trends/scripts/lib/x_api.py +824 -0
- package/skills/trends/scripts/lib/x_envelope.py +733 -0
- package/skills/trends/scripts/lib/x_judge.py +292 -0
- package/skills/trends/scripts/lib/xai_x.py +26 -3
- package/skills/trends/scripts/lib/xquik.py +89 -15
- package/skills/trends/scripts/lib/xurl_x.py +284 -91
- package/skills/trends/scripts/lib/youtube_yt.py +570 -119
- package/skills/trends/scripts/setup-keychain.sh +12 -4
- package/skills/trends/scripts/setup-pass.sh +3 -0
- package/skills/trends/scripts/store.py +444 -50
- package/skills/trends/scripts/watchlist.py +38 -11
- package/skills/trends/scripts/x_lane_parity.py +107 -0
- package/skills/video-studio/CHANGELOG.md +4 -0
- package/skills/video-studio/SKILL.md +4 -4
- package/skills/video-studio/references/styles/product-flight.md +45 -0
- package/skills/video-studio/references/styles.md +3 -1
- package/skills/video-studio-motion/ATTRIBUTION.md +1 -1
- package/skills/video-studio-motion/CHANGELOG.md +4 -0
- package/skills/video-studio-motion/SKILL.md +8 -5
- package/skills/youtube-planner/ATTRIBUTION.md +25 -0
- package/skills/youtube-planner/CHANGELOG.md +12 -0
- package/skills/youtube-planner/SKILL.md +191 -0
- package/skills/youtube-planner/references/plan-template.md +57 -0
- package/skills/youtube-planner/references/stages.md +119 -0
- package/skills/youtube-planner/scripts/floor_ceiling.py +132 -0
- package/skills/coder-brainstorming/spec-document-reviewer-prompt.md +0 -49
- package/skills/trends/scripts/lib/reddit_rss.py +0 -224
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import math
|
|
6
|
+
from collections.abc import Iterable, Mapping
|
|
6
7
|
|
|
7
8
|
from . import dates, relevance, schema
|
|
8
9
|
|
|
@@ -11,11 +12,19 @@ from . import dates, relevance, schema
|
|
|
11
12
|
SOURCE_QUALITY = {
|
|
12
13
|
"xiaohongshu": 0.7,
|
|
13
14
|
"hackernews": 0.8,
|
|
15
|
+
"devto": 0.78,
|
|
14
16
|
"youtube": 0.85,
|
|
15
17
|
"digg": 0.85,
|
|
16
18
|
"arxiv": 0.9,
|
|
17
19
|
"techmeme": 0.85,
|
|
18
20
|
"trustpilot": 0.78,
|
|
21
|
+
# Verified-purchase reviews on a live aggregate rating: high-quality
|
|
22
|
+
# buyer evidence, a notch above Trustpilot's open review model.
|
|
23
|
+
"amazon": 0.8,
|
|
24
|
+
# Paid creative is first-party marketing copy: a reliable statement of
|
|
25
|
+
# what the brand is pushing, but not independent evidence about it, so it
|
|
26
|
+
# sits below the review-backed sources and above open social.
|
|
27
|
+
"meta_ads": 0.72,
|
|
19
28
|
"reddit": 0.6,
|
|
20
29
|
"x": 0.68,
|
|
21
30
|
"bluesky": 0.66,
|
|
@@ -24,6 +33,7 @@ SOURCE_QUALITY = {
|
|
|
24
33
|
"instagram": 0.58,
|
|
25
34
|
"tiktok": 0.58,
|
|
26
35
|
"jobs": 0.72,
|
|
36
|
+
"corpus": 0.75,
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
|
|
@@ -47,7 +57,7 @@ def local_relevance(
|
|
|
47
57
|
# often have titles that don't keyword-match the query (e.g., "YE - FATHER
|
|
48
58
|
# (feat. TRAVIS SCOTT)" doesn't match "kanye west"). The engagement signals
|
|
49
59
|
# say "this is important" even when text overlap is weak.
|
|
50
|
-
if item.source == "youtube" and item.engagement.get("views"
|
|
60
|
+
if item.source == "youtube" and (item.engagement.get("views") or 0) > 100_000:
|
|
51
61
|
score = max(score, 0.3)
|
|
52
62
|
|
|
53
63
|
# Project-mode GitHub floor: items fetched via --github-repo are explicitly
|
|
@@ -58,6 +68,17 @@ def local_relevance(
|
|
|
58
68
|
if "project-mode" in labels:
|
|
59
69
|
score = max(score, 0.8)
|
|
60
70
|
|
|
71
|
+
# Grounding-exempt floor (currently Amazon): the adapter already gated
|
|
72
|
+
# these against the model-supplied product keyword before creating them,
|
|
73
|
+
# so they are relevant by construction. Their text is marketing copy plus
|
|
74
|
+
# buyer reviews, which rarely repeats the topic phrasing -- a "Weber
|
|
75
|
+
# Grills" run surfaces a product named "Spirit E-325" whose reviews talk
|
|
76
|
+
# about searing, not about Weber. Without the floor, correctly-retrieved
|
|
77
|
+
# evidence gets pruned for failing a keyword match it was never going to
|
|
78
|
+
# win. Mirrors the project-mode GitHub floor above.
|
|
79
|
+
if isinstance(item.metadata, dict) and item.metadata.get("grounding_exempt"):
|
|
80
|
+
score = max(score, 0.8)
|
|
81
|
+
|
|
61
82
|
return score
|
|
62
83
|
|
|
63
84
|
|
|
@@ -68,6 +89,11 @@ def freshness(
|
|
|
68
89
|
reference_date: str | None = None,
|
|
69
90
|
max_days: int = 30,
|
|
70
91
|
) -> int:
|
|
92
|
+
# A live repo snapshot dated to the window end (no in-window release) has a
|
|
93
|
+
# read time, not an event date: rank it at the scale's midpoint instead of
|
|
94
|
+
# maximum freshness, or every pinned repo card sorts to #1.
|
|
95
|
+
if isinstance(item.metadata, dict) and item.metadata.get("date_is_snapshot"):
|
|
96
|
+
return 50
|
|
71
97
|
score = dates.recency_score(
|
|
72
98
|
item.published_at,
|
|
73
99
|
max_days=max_days,
|
|
@@ -109,6 +135,7 @@ def _top_comment_score(item: schema.SourceItem) -> float:
|
|
|
109
135
|
_VOTE_LOG_REFERENCE: dict[str, float] = {
|
|
110
136
|
"reddit": 7.6, # ~log1p(2000)
|
|
111
137
|
"hackernews": 6.2, # ~log1p(500)
|
|
138
|
+
"devto": 3.0, # ~log1p(20) direct replies; the API has no comment likes
|
|
112
139
|
"youtube": 10.3, # ~log1p(30000)
|
|
113
140
|
"tiktok": 10.3, # ~log1p(30000)
|
|
114
141
|
"instagram": 9.2, # ~log1p(10000)
|
|
@@ -161,11 +188,18 @@ ENGAGEMENT_WEIGHTS: dict[str, list[tuple[str, float]]] = {
|
|
|
161
188
|
"x": [("likes", 0.55), ("reposts", 0.25), ("replies", 0.15), ("quotes", 0.05)],
|
|
162
189
|
"instagram": [("views", 0.50), ("likes", 0.30), ("comments", 0.20)],
|
|
163
190
|
"hackernews": [("points", 0.55), ("comments", 0.45)],
|
|
191
|
+
"devto": [("reactions", 0.60), ("comments", 0.40)],
|
|
164
192
|
"bluesky": [("likes", 0.40), ("reposts", 0.30), ("replies", 0.20), ("quotes", 0.10)],
|
|
165
193
|
"truthsocial": [("likes", 0.45), ("reposts", 0.30), ("replies", 0.25)],
|
|
166
194
|
"polymarket": [("volume", 0.60), ("liquidity", 0.40)],
|
|
167
195
|
"digg": [("postCount", 0.40), ("uniqueAuthors", 0.30), ("rank_score", 0.30)],
|
|
168
196
|
"trustpilot": [("reviews", 1.0)],
|
|
197
|
+
"amazon": [("ratings", 1.0)],
|
|
198
|
+
# Meta publishes reach and spend only for political and issue ads, so a
|
|
199
|
+
# commercial creative has no audience signal at all. How many variants a
|
|
200
|
+
# brand cut of one creative is the closest honest proxy for how hard it is
|
|
201
|
+
# pushing that message.
|
|
202
|
+
"meta_ads": [("variants", 1.0)],
|
|
169
203
|
}
|
|
170
204
|
|
|
171
205
|
|
|
@@ -209,6 +243,19 @@ def _tiktok_engagement(item: schema.SourceItem) -> float | None:
|
|
|
209
243
|
return (0.45 * views) + (0.27 * likes) + (0.18 * comments) + (0.10 * top_comment)
|
|
210
244
|
|
|
211
245
|
|
|
246
|
+
def _instagram_engagement(item: schema.SourceItem) -> float | None:
|
|
247
|
+
# Mirrors _tiktok_engagement: reels are video-shaped, and a highly-liked top
|
|
248
|
+
# comment carves out 10% of the signal (via comment_like_count -> score) so
|
|
249
|
+
# crowd-loved IG comments lift their post's ranking like YouTube/TikTok.
|
|
250
|
+
views = log1p_safe(item.engagement.get("views"))
|
|
251
|
+
likes = log1p_safe(item.engagement.get("likes"))
|
|
252
|
+
comments = log1p_safe(item.engagement.get("comments"))
|
|
253
|
+
top_comment = _top_comment_score(item)
|
|
254
|
+
if not any([views, likes, comments, top_comment]):
|
|
255
|
+
return None
|
|
256
|
+
return (0.45 * views) + (0.27 * likes) + (0.18 * comments) + (0.10 * top_comment)
|
|
257
|
+
|
|
258
|
+
|
|
212
259
|
def _generic_engagement(item: schema.SourceItem) -> float | None:
|
|
213
260
|
if not item.engagement:
|
|
214
261
|
return None
|
|
@@ -225,12 +272,120 @@ def engagement_raw(item: schema.SourceItem) -> float | None:
|
|
|
225
272
|
return _youtube_engagement(item)
|
|
226
273
|
if item.source == "tiktok":
|
|
227
274
|
return _tiktok_engagement(item)
|
|
275
|
+
if item.source == "instagram":
|
|
276
|
+
return _instagram_engagement(item)
|
|
228
277
|
weights = ENGAGEMENT_WEIGHTS.get(item.source)
|
|
229
278
|
if weights:
|
|
230
279
|
return _weighted_engagement(item, weights)
|
|
231
280
|
return _generic_engagement(item)
|
|
232
281
|
|
|
233
282
|
|
|
283
|
+
# Per-source log reference for engagement_score, in each source's own units.
|
|
284
|
+
# Same convention as _VOTE_LOG_REFERENCE (which it extends): the value is
|
|
285
|
+
# engagement_raw for a "very high" item, so that item scores 100 and, the scale
|
|
286
|
+
# being logarithmic, roughly its square root scores 50. Every source that can
|
|
287
|
+
# reach engagement_raw is listed here or in _NO_ENGAGEMENT_SCORE_SOURCES; an
|
|
288
|
+
# unlisted source gets no score rather than an assumed Reddit-vote scale.
|
|
289
|
+
_ENGAGEMENT_LOG_REFERENCE: dict[str, float] = {
|
|
290
|
+
**_VOTE_LOG_REFERENCE,
|
|
291
|
+
# Microblog likes/reposts/replies run on X's scale (~10k likes is very high).
|
|
292
|
+
"truthsocial": 9.2, # ~log1p(10000)
|
|
293
|
+
"threads": 9.2, # ~log1p(10000)
|
|
294
|
+
# Likes/comments/favorites on an Instagram-like feed.
|
|
295
|
+
"xiaohongshu": 9.2, # ~log1p(10000)
|
|
296
|
+
# Saves on a viral pin reach ~10k.
|
|
297
|
+
"pinterest": 9.2, # ~log1p(10000)
|
|
298
|
+
# Reactions run like Reddit upvotes: a viral post has ~2k.
|
|
299
|
+
"linkedin": 7.6, # ~log1p(2000)
|
|
300
|
+
# Per-issue/PR reactions and comments (repo snapshot cards, whose counts
|
|
301
|
+
# are lifetime stars, get no score: see is_repo_snapshot). A
|
|
302
|
+
# ~100-reaction thread lands near 50.
|
|
303
|
+
"github": 9.2, # ~log1p(10000)
|
|
304
|
+
# Dollars, not votes. polymarket.py's own saturation anchors: $9M volume
|
|
305
|
+
# (log1p 16) and $1.2M liquidity (log1p 14), weighted 0.6/0.4 as in
|
|
306
|
+
# ENGAGEMENT_WEIGHTS: 0.6*16 + 0.4*14 = 15.2.
|
|
307
|
+
"polymarket": 15.2,
|
|
308
|
+
# A rank-1 cluster (rank_score 50) with ~100 posts from ~100 authors:
|
|
309
|
+
# 0.4*log1p(100) + 0.3*log1p(100) + 0.3*log1p(50) = 4.4.
|
|
310
|
+
"digg": 4.4,
|
|
311
|
+
# Lifetime totals for a whole company or product, not one post: big
|
|
312
|
+
# consumer brands and best-sellers carry ~100k, so ~300 lands near 50.
|
|
313
|
+
"trustpilot": 11.5, # ~log1p(100000) reviews
|
|
314
|
+
"amazon": 11.5, # ~log1p(100000) ratings
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
# Sources whose engagement fields are not an audience response, so no
|
|
318
|
+
# engagement score is comparable with votes or likes:
|
|
319
|
+
# jobs - a constant open_roles=1 per posting
|
|
320
|
+
# meta_ads - variant count measures the advertiser's push, not reach
|
|
321
|
+
# grounding, perplexity - web results; counts are citations, not readers
|
|
322
|
+
# stocktwits - the author's follower count dominates sparse likes/reshares
|
|
323
|
+
# telegram - the channel's subscriber count dominates per-post views
|
|
324
|
+
# arxiv, techmeme, dripstack, corpus - no engagement fields at all
|
|
325
|
+
_NO_ENGAGEMENT_SCORE_SOURCES = frozenset({
|
|
326
|
+
"jobs", "meta_ads", "grounding", "perplexity", "stocktwits", "telegram",
|
|
327
|
+
"arxiv", "techmeme", "dripstack", "corpus",
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
# YouTube, TikTok, and Instagram weight log1p(views) at 0.45 in
|
|
332
|
+
# engagement_raw, and views run orders of magnitude above votes: any upload
|
|
333
|
+
# collects ~100 passive feed views, so a 117-view, 1-like video already had raw
|
|
334
|
+
# 2.4 (23 on the vote-style 10.3 reference, three times a 3-like X post).
|
|
335
|
+
# These sources map raw linearly from a floor (an upload with ~100 views and no
|
|
336
|
+
# reactions: 0.45*log1p(100) = 2.1) to a "very high" month-window post (~2-3M
|
|
337
|
+
# views, ~100k likes, ~3k comments: 0.45*14.9 + 0.32*11.5 + 0.13*8.0 = 11.4).
|
|
338
|
+
# Resulting scores: 117 views/1 like ~3, 655 views/17 likes ~17, 133k views/
|
|
339
|
+
# 8.4k likes ~70, 726k views/13k likes ~85, 1M+ views ~90-100.
|
|
340
|
+
_VIEW_ENGAGEMENT_SOURCES = frozenset({"youtube", "tiktok", "instagram"})
|
|
341
|
+
_VIEW_ENGAGEMENT_LOG_FLOOR = 2.1
|
|
342
|
+
_VIEW_ENGAGEMENT_LOG_REFERENCE = 11.5
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def is_repo_snapshot(item: schema.SourceItem) -> bool:
|
|
346
|
+
"""True for a GitHub repo card (project mode, person-mode repo items).
|
|
347
|
+
|
|
348
|
+
Their engagement is lifetime stars and the open-issue count, a snapshot
|
|
349
|
+
of the repo's whole history rather than an audience response inside the
|
|
350
|
+
window. Issues and PRs carry reactions/comments, never stars.
|
|
351
|
+
"""
|
|
352
|
+
return (
|
|
353
|
+
item.source == "github"
|
|
354
|
+
and "stars" in (item.engagement or {})
|
|
355
|
+
and not (item.metadata or {}).get("is_pr")
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def engagement_score(item: schema.SourceItem) -> int | None:
|
|
360
|
+
"""Engagement on an absolute 0-100 scale, comparable across streams.
|
|
361
|
+
|
|
362
|
+
``engagement_raw`` is a weighted mean of log1p counts, so dividing it by
|
|
363
|
+
the source's log reference (``_ENGAGEMENT_LOG_REFERENCE``, in that
|
|
364
|
+
source's own units) places a 250-upvote Reddit thread near 50 and a 3-like
|
|
365
|
+
X post near 8 no matter what else shares their stream. The per-stream
|
|
366
|
+
min-max rescale this replaces mapped every stream's best item to 100 and
|
|
367
|
+
a lone item to 50, so the score said nothing about actual engagement.
|
|
368
|
+
View-weighted sources (YouTube, TikTok, Instagram) use the view scale
|
|
369
|
+
(``_VIEW_ENGAGEMENT_LOG_FLOOR`` to ``_VIEW_ENGAGEMENT_LOG_REFERENCE``)
|
|
370
|
+
instead of their vote reference. Sources without a comparable audience
|
|
371
|
+
signal, and GitHub repo snapshots (lifetime stars), return None.
|
|
372
|
+
"""
|
|
373
|
+
if item.source in _NO_ENGAGEMENT_SCORE_SOURCES or is_repo_snapshot(item):
|
|
374
|
+
return None
|
|
375
|
+
ref = _ENGAGEMENT_LOG_REFERENCE.get(item.source)
|
|
376
|
+
if ref is None:
|
|
377
|
+
return None
|
|
378
|
+
raw = engagement_raw(item)
|
|
379
|
+
if raw is None:
|
|
380
|
+
return None
|
|
381
|
+
if item.source in _VIEW_ENGAGEMENT_SOURCES:
|
|
382
|
+
floor = _VIEW_ENGAGEMENT_LOG_FLOOR
|
|
383
|
+
fraction = (raw - floor) / (_VIEW_ENGAGEMENT_LOG_REFERENCE - floor)
|
|
384
|
+
else:
|
|
385
|
+
fraction = raw / ref
|
|
386
|
+
return int(round(100.0 * max(0.0, min(1.0, fraction))))
|
|
387
|
+
|
|
388
|
+
|
|
234
389
|
def normalize(values: list[float | None]) -> list[int | None]:
|
|
235
390
|
valid = [value for value in values if value is not None]
|
|
236
391
|
if not valid:
|
|
@@ -256,8 +411,8 @@ def annotate_stream(
|
|
|
256
411
|
) -> list[schema.SourceItem]:
|
|
257
412
|
"""Attach local scoring metadata and return items sorted by local_rank_score."""
|
|
258
413
|
prepared_query = ranking_query if isinstance(ranking_query, relevance.PreparedQuery) else relevance.PreparedQuery(ranking_query)
|
|
259
|
-
|
|
260
|
-
|
|
414
|
+
for item in items:
|
|
415
|
+
eng_score = engagement_score(item)
|
|
261
416
|
item.local_relevance = local_relevance(item, prepared_query)
|
|
262
417
|
item.freshness = freshness(
|
|
263
418
|
item,
|
|
@@ -295,23 +450,60 @@ def _passes_engagement_floor(item: schema.SourceItem, sole_source: bool) -> bool
|
|
|
295
450
|
return True
|
|
296
451
|
if sole_source:
|
|
297
452
|
return True
|
|
298
|
-
views = item.engagement.get("views"
|
|
453
|
+
views = item.engagement.get("views") or 0 if item.engagement else 0
|
|
299
454
|
return views >= _VIDEO_ENGAGEMENT_FLOOR_VIEWS
|
|
300
455
|
|
|
301
456
|
|
|
302
457
|
def prune_low_relevance(
|
|
303
458
|
items: list[schema.SourceItem],
|
|
304
459
|
minimum: float = 0.15,
|
|
460
|
+
first_party_handles: Iterable[str] | None = None,
|
|
461
|
+
first_party_by_source: Mapping[str, Iterable[str]] | None = None,
|
|
305
462
|
) -> list[schema.SourceItem]:
|
|
306
463
|
"""Drop weak lexical matches when stronger evidence exists.
|
|
307
464
|
|
|
308
|
-
Social-source items with zero engagement get a stricter
|
|
309
|
-
because zero engagement on a social platform is a strong noise
|
|
465
|
+
Social-source items with genuinely zero engagement get a stricter
|
|
466
|
+
threshold because zero engagement on a social platform is a strong noise
|
|
467
|
+
signal.
|
|
310
468
|
|
|
311
469
|
TikTok and Instagram items with fewer than 1000 views are pruned
|
|
312
470
|
(unless they are the only source represented in the batch).
|
|
471
|
+
|
|
472
|
+
``first_party_handles`` names accounts this run is explicitly searching
|
|
473
|
+
(the subject of the topic). Their own posts are exempt from the floor: a
|
|
474
|
+
post almost never contains its own author's name, so lexical relevance
|
|
475
|
+
scores it at or near zero no matter how on-topic it is. Without the
|
|
476
|
+
exemption a mixed batch loses them silently, because the ``filtered or
|
|
477
|
+
items`` rescue below only fires when *every* item fails.
|
|
478
|
+
|
|
479
|
+
``first_party_by_source`` scopes exemptions to one platform each (e.g.
|
|
480
|
+
``--ig-creators`` names Instagram accounts). A creator flag must not
|
|
481
|
+
exempt a same-name account on a *different* platform: username reuse
|
|
482
|
+
across platforms is common, and an unrelated account would otherwise
|
|
483
|
+
bypass the relevance and engagement floors.
|
|
313
484
|
"""
|
|
314
485
|
sources_present = {item.source for item in items}
|
|
486
|
+
first_party = {
|
|
487
|
+
h.strip().lstrip("@").lower()
|
|
488
|
+
for h in (first_party_handles or ())
|
|
489
|
+
if h and h.strip()
|
|
490
|
+
}
|
|
491
|
+
scoped_first_party = {
|
|
492
|
+
source: {
|
|
493
|
+
h.strip().lstrip("@").lower()
|
|
494
|
+
for h in handles
|
|
495
|
+
if h and h.strip()
|
|
496
|
+
}
|
|
497
|
+
for source, handles in (first_party_by_source or {}).items()
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
def _is_first_party(item: schema.SourceItem) -> bool:
|
|
501
|
+
if not item.author:
|
|
502
|
+
return False
|
|
503
|
+
author = item.author.strip().lstrip("@").lower()
|
|
504
|
+
if author in first_party:
|
|
505
|
+
return True
|
|
506
|
+
return author in scoped_first_party.get(item.source, frozenset())
|
|
315
507
|
|
|
316
508
|
def passes(item: schema.SourceItem) -> bool:
|
|
317
509
|
# YouTube items with successfully extracted transcripts should not
|
|
@@ -319,10 +511,17 @@ def prune_low_relevance(
|
|
|
319
511
|
# already proves substantive topical coverage.
|
|
320
512
|
if item.source == "youtube" and item.snippet:
|
|
321
513
|
return True
|
|
514
|
+
# Posts by an account this run is explicitly searching are evidence by
|
|
515
|
+
# provenance, not by lexical overlap with the topic.
|
|
516
|
+
if _is_first_party(item):
|
|
517
|
+
return True
|
|
322
518
|
rel = item.local_relevance if item.local_relevance is not None else 0.0
|
|
323
519
|
if rel < minimum:
|
|
324
520
|
return False
|
|
325
|
-
|
|
521
|
+
# Key the stricter social gate on genuinely absent engagement (no
|
|
522
|
+
# counts at all), not on a rounded score: a handful of likes is still
|
|
523
|
+
# engagement even where the 0-100 scale rounds it low.
|
|
524
|
+
if item.source in _SOCIAL_SOURCES and not engagement_raw(item):
|
|
326
525
|
if rel < minimum * 1.5:
|
|
327
526
|
return False
|
|
328
527
|
sole_source = sources_present == {item.source}
|