949bcf8942
* feat: vs mode runs N full passes; --competitors wraps vs with auto-discovery
Unifies vs-mode and --competitors onto one fanout architecture. A topic
containing "vs" / "versus" now runs N full pipeline.run() calls in parallel
(reverting the one-pass latency optimization that removed per-entity
depth); --competitors becomes a SKILL.md-level shortcut where the hosting
reasoning model (Claude Code, Codex, Hermes, Gemini) discovers N peers via
its own WebSearch, runs Step 0.55 per entity, and invokes the engine with
a vs-topic + --competitors-plan JSON.
Changed:
- vs-mode: N full passes in parallel via fanout (was 1 merged pass).
- --competitors: SKILL.md shortcut for vs-mode-with-discovery. Engine flag
kept for headless/cron use. LAW 7-style stderr reframed to lead with the
hosting-model path (use WebSearch + --competitors-plan) instead of
BRAVE_API_KEY. Footer BRAVE/SERPER nudge suppressed when --plan or
--competitors-plan present (hosting model already has WebSearch).
Added:
- --competitors-plan JSON flag: per-entity {x_handle, x_related, subreddits,
github_user, github_repos, context}. Accepts inline JSON or file path.
subrun_kwargs_for helper is the single source of truth for per-entity
kwargs — no closure-default fallthrough from main scope.
- Per-entity save files: each entity's sub-run produces its own
{slug}-raw.md with a single-row Resolved Entities block.
- --polymarket-keywords filter for ambiguous single-token topics.
Fixed:
- test_competitor_subrun_isolation regression suite locks in 3.0.12's
no-leak invariant (main flags do not inherit into peer sub-runs).
- Updates test_regression.py for the new comparison-mode payload shape.
Bumps plugin.json to 3.0.13. 1,219 tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: comparison title attribution — (Last 30 Days) → (/Last30Days)
User feedback on 3.0.13 dogfood runs (Kanye vs Drake, Mercer Island,
Figma): the comparison-mode synthesis title should attribute to the
slash command rather than restate the date range.
Three SKILL.md occurrences updated. Pure documentation change. Bumps to
3.0.14.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
75 lines
2.5 KiB
Python
75 lines
2.5 KiB
Python
# ruff: noqa: E402
|
|
"""Tests for --polymarket-keywords filter and filter_items_against_keywords."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
|
|
|
from lib import polymarket
|
|
|
|
|
|
def _item(title: str) -> dict:
|
|
return {"title": title}
|
|
|
|
|
|
class FilterItemsAgainstKeywordsTests(unittest.TestCase):
|
|
def test_no_keywords_returns_all(self):
|
|
items = [_item("NBA Finals"), _item("Glasgow Warriors")]
|
|
out = polymarket.filter_items_against_keywords(items, [])
|
|
self.assertEqual(out, items)
|
|
|
|
def test_single_keyword_filters(self):
|
|
items = [
|
|
_item("Golden State Warriors win title"),
|
|
_item("Glasgow Warriors rugby"),
|
|
_item("Honor of Kings: Rogue Warriors"),
|
|
]
|
|
out = polymarket.filter_items_against_keywords(items, ["golden"])
|
|
self.assertEqual(len(out), 1)
|
|
self.assertIn("Golden State", out[0]["title"])
|
|
|
|
def test_multiple_keywords_any_match(self):
|
|
items = [
|
|
_item("NBA Finals: Warriors vs Celtics"),
|
|
_item("Glasgow rugby"),
|
|
_item("GSW schedule"),
|
|
]
|
|
out = polymarket.filter_items_against_keywords(items, ["nba", "gsw"])
|
|
self.assertEqual(len(out), 2)
|
|
|
|
def test_case_insensitive_match(self):
|
|
items = [_item("Golden State Warriors"), _item("GLASGOW WARRIORS")]
|
|
out = polymarket.filter_items_against_keywords(items, ["GOLDEN"])
|
|
self.assertEqual(len(out), 1)
|
|
self.assertIn("Golden State", out[0]["title"])
|
|
|
|
def test_empty_keyword_strings_ignored(self):
|
|
items = [_item("NBA Finals")]
|
|
out = polymarket.filter_items_against_keywords(items, ["", " ", ""])
|
|
# All keywords are empty → treated as no filter
|
|
self.assertEqual(out, items)
|
|
|
|
def test_sourceitem_like_objects(self):
|
|
class _SI:
|
|
def __init__(self, t):
|
|
self.title = t
|
|
|
|
items = [_SI("NBA Finals"), _SI("Glasgow Warriors rugby")]
|
|
out = polymarket.filter_items_against_keywords(items, ["nba"])
|
|
self.assertEqual(len(out), 1)
|
|
self.assertEqual(out[0].title, "NBA Finals")
|
|
|
|
def test_no_match_returns_empty(self):
|
|
items = [_item("Glasgow Warriors"), _item("Rogue Warriors")]
|
|
out = polymarket.filter_items_against_keywords(items, ["nba", "gsw"])
|
|
self.assertEqual(out, [])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|