Skip to content

Releases: jbdrak/ssb-for-agents

v2.9.3

Choose a tag to compare

@jbdrak jbdrak released this 05 Sep 14:37

Highlights

  • scan tier filter — tennis fallback now honors -t; -t 1 scans stop listing TIER 2 plays.
  • massive JSON cut — unresolved rows are summarized past 50 (total/byReason/sample) instead of a ~12 MB dump on broad scans.
  • budget actually spendable — the odds-history gate was serial (~10 calls/sec), capping every scan regardless of budget. Now parallel (PP_ODDS_HISTORY_CONCURRENCY, default 3). Real upstream 429s still halt the gate with cooldown.
  • Also in this line: per-pair hydration budget bounded by the caller's limit (v2.9.2).

3093/3093 tests, lint/types/format clean. See CHANGELOG.md.

GitHub-only release; npm publish is not wired (no NPM_TOKEN).

v2.9.2

Choose a tag to compare

@jbdrak jbdrak released this 05 Sep 06:23

Highlights

  • fix — mixed quick_screen scans (pp scan mlb wnba ncaaf -n 5) timed out past 150s with 1000+ "scan scope aborted" and never emitted JSON. The per-pair hydration budget (133 games/pair) flooded the serial odds-history gate. Now bounded by the caller's limit: ~33s, 0 aborts, honest output.
  • fix — EV-first discovery pass capped per-pair the same way; direct/targeted scans keep the full pass.
  • Deps: @types/node, eslint, globals, actions/setup-python bumps (#46, #47).

Full suite 3083/3083, lint/types/format clean. See CHANGELOG.md for details.

Note: published as a GitHub release only; npm publish is not wired up (no NPM_TOKEN).

v2.1.7

Choose a tag to compare

@github-actions github-actions released this 16 Jun 06:28

Full Changelog: v2.1.6...v2.1.7

v2.1.6

Choose a tag to compare

@jbdrak jbdrak released this 15 Jun 19:41
8cecc00

2.1.6

Consensus-preservation fix in extractScreenRows — every main-line screen row was silently cascading to consensusBookCount: 0 / TIER 4 / PASS because the full per-book odds map was being clobbered on the expanded row. Symptom: live screen / get_play_details / recommended_bets / sharp_plays calls all returned consensusBookCount: 0, consensusEdge: null, executionQuality: "unknown", marketBookCount: 0, supportBookCount: 0, screenScore: 0, gatePassed: false with gateReason: "score 0.00 below 2.05 gate", and consequently riskScore: 10, kaiCall: "PASS", confidenceTier: "TIER 4". The same symptom pattern as the 2026-06-15 upstream consensus outage — but the upstream /odds_history_new and /screen are healthy, so the bug was local.

Root cause

extractScreenRows (lib/screen-parser.js) expands a normalized upstream row (selections: { null: { ...lifted fields, odds: {full map} } }, defaultKey: null) into per-book rows by spreading the row and then setting odds: bookOdds.odds1 (a number). The explicit odds override clobbered the lifted full map. Combined with normalizeRow setting selections: undefined (because no other selection keys exist), the expanded row had no way for the ranker to find the full map. The ranker (expandScreenRow in lib/screen-ranker.js) gates on row?.selections && (row?.book || row?.sportsbook); with selections: undefined the main path was skipped, the early-return on missing preferredOdds fired, and the row passed through with every consensus-derived field at zero.

Fix

  • lib/screen-parser.js extractScreenRows (isNormalizedNonProp branch) — preserve the full lifted odds map on the expanded row as allBookOdds before overriding odds with the per-book number.
  • lib/screen-ranker.js expandScreenRow — when row.selections is undefined but row.allBookOdds is present, reconstruct the selections: { null: { ...lifted fields, odds: row.allBookOdds } } shape the existing main path already understands.
  • 3 new regression tests in test/propprofessor-analysis.test.js (live-shape fixture mirroring the actual /screen payload, v2.1.2 fallback preservation, per-book odds contract preserved).

Live impact

Field Before After
consensusBookCount 0 5–19
consensusStrength "none" "strong"
consensusEdge null 0.4–5.2pp
executionQuality "unknown" "best" / "playable" / "bad"
marketBookCount 0 4–19
supportBookCount 0 4–19
bestAvailableOdds null real value (e.g. 117 for a +116 side)
screenScore 0 2.4–11.8
gatePassed false ("score 0.00 below 2.05 gate") true ("score X.XX passed 2.05 gate")
riskScore 10 4–8
kaiCall "PASS" "CONSIDER" / "BET" (when signals warrant)
confidenceTier "TIER 4" "TIER 1""TIER 3"
tierTrajectory all-zero placeholder real trend / volatility / data points
scoreBreakdown all-zero real consensus / movement / sport / freshness
"No consensus data" warning always present gone (only "No line history" + freshness remain when applicable)

What I'm NOT touching (separate concerns)

  • < 2 gate in resolveHistoryForEntity — correct (you need 2+ points for movement). With consensus now flowing, rows with sparse history rank on consensus + freshness + sport score instead of being silently killed.
  • freshnessFallbackUsed: true for the screen call itself — separate upstream timestamp issue.

Full Changelog: v2.1.5...v2.1.6

What's Changed

  • chore(deps-dev): bump prettier from 3.8.3 to 3.8.4 in the npm-minor-and-patch group across 1 directory by @dependabot[bot] in #30
  • chore(deps-dev): bump eslint from 9.39.4 to 10.5.0 by @dependabot[bot] in #31

Full Changelog: v2.1.5...v2.1.6

v2.1.5

Choose a tag to compare

@github-actions github-actions released this 15 Jun 01:16

Full Changelog: v2.1.4...v2.1.5

v2.1.4 — Warning was broken, now it's not

Choose a tag to compare

@github-actions github-actions released this 14 Jun 21:31

v2.1.4 — Warning was broken, now it's not

Hotfix on top of v2.1.3. v2.1.3 shipped the line-history backfill and a degraded-data warning. Live testing caught that the warning was silently filtering out every Puck Line / Run Line / Total Goals / Total Runs row — the check used r.line1 == null as a defense-in-depth moneyline guard, but the ranker doesn't surface line1 on the output for Puck Line rows (the normalizeRow step only lifts selections.null.*, and Puck Line defaultKey is "-1" / "-3.5", not "null"). The check evaluated undefined == null === true and excluded every line-based row. Test data included line1 so unit tests passed; live MCP calls never got the warning. Fixed in this release.

What changed for users

  • The v2.1.3 "Line values missing from upstream" warning now actually fires for non-moneyline markets. If you're using a Puck Line / Run Line / Point Spread / Total screen, you'll see the degraded-state warning instead of silent line: null everywhere.

What's under the hood

  • buildDegradedDataWarnings in lib/propprofessor-mcp-ranked-screen.js no longer inspects r.line1 or r.line directly. It relies on r.lineFieldMissingCount > 0 as the primary signal (the backfill code in resolveHistoryForEntity already guards on fallbackLine !== null, which is naturally null for moneylines, so the count is naturally 0 there) plus a defense-in-depth market === "moneyline" check.
  • Regression test in test/propprofessor-mcp-ranked-screen.test.js mirrors the real ranked-row shape (no line1, large backfill count). The old tests had line1: -1 set on the synthetic data, which masked the production bug.

Honest framing

This is the second time a unit test passed but live data didn't. v2.1.3's release notes claimed the degraded-data warning would fire; it did not, because the test data didn't match the live shape. Live testing caught it. v2.1.4 closes the loop. v2.1.3 was technically correct (backfill works) but the user-facing warning was broken.

Also updates the v2.1.1 release page to add an "Honest framing" section about the incomplete line-history tracking in that release, cross-referencing v2.1.3 and v2.1.4.

Try it

make install to upgrade, or npm install from a clean clone.

Then ask your agent: "Show me the line history on tonight's NHL Puck Line." You should now see the degraded-data warning in the response.


Full diff in CHANGELOG.md.

v2.1.3 — Line-history backfill + degraded-data warning

Choose a tag to compare

@github-actions github-actions released this 14 Jun 21:10

v2.1.3 — Line-history backfill + degraded-data warning

The PropProfessor backend's /odds_history endpoint doesn't return a line field per entry — only odds, start_ts, end_ts, and liquidity. For line-based markets (Puck Line, Run Line, Point Spread, Total Goals/Runs/Rounds) that meant every history entry had line: null and the MCP couldn't track line movement. v2.1.1 / v2.1.2 shipped the spread-alias fix but the underlying line-history data was missing upstream. v2.1.3 closes the loop: self-consistent entries + a visible degraded-state warning.

What changed for users

  • Line-based markets now show non-null line values in their history entries. The MCP backfills the current line (from the screen response) into every history entry that was missing it. Your lineHistory arrays are now self-consistent.
  • Degraded-state warning in the response. When the MCP is operating on backfilled data, resultMeta.warnings now reads: "Line values missing from upstream history for N/M non-moneyline rows (K entries backfilled from current line). Line-movement detection is degraded for this slate." You see the degraded state explicitly instead of a silent line: null everywhere.

What's under the hood

  • lib/propprofessor-history.js: resolveHistoryForEntity reads the matched row's current line and backfills it into any history entry where entry.line === null. Tracks the count as lineFieldMissingCount. Moneylines (legitimate line: null) are not backfilled.
  • lib/propprofessor-screen-history.js: propagates the backfill count through hydrateScreenRowsWithHistory.
  • lib/propprofessor-mcp-ranked-screen.js: buildDegradedDataWarnings emits a warning when non-moneyline rows had line values backfilled.
  • 6 new regression tests covering: backfill from current line, no-backfill when upstream provides line, no-backfill on moneylines, warning fires correctly, warning suppressed for moneylines, warning suppressed when no backfill happened.

Honest framing

The v2.1.1 / v2.1.2 release notes claimed a "spread-alias regression fix" that resolved MARKET_ALIASES.spread / .handicap to per-league canonical names. The alias resolution is correct. The line-movement tracking it was implicitly claiming to enable is not — the upstream data doesn't carry per-entry line values. v2.1.3 makes this honest: the alias works, the line-history entries are self-consistent, and the degraded state is visible. The upstream fix is tracked separately.

Try it

make install to upgrade, or npm install from a clean clone.

Then ask your agent: "Show me the line history on tonight's NHL Puck Line." You should see the entries populated and (if the slate is thin) a warning explaining the backfill.


Full diff in CHANGELOG.md.

v2.1.2 — UFC card now actually shows up

Choose a tag to compare

@github-actions github-actions released this 14 Jun 20:34

v2.1.2 — UFC card now actually shows up

Hotfix shipped ~2 hours after v2.1.1. A live test against tonight's UFC card caught a bug where screen_ranked returned 0 rows for any non-major league (UFC, Soccer) — the default focus book was being set to Pinnacle, and Pinnacle doesn't post UFC moneylines. Algorithm, tier system, and tool surface unchanged.

What changed for users

  • screen_ranked now returns UFC data. Calling screen_ranked({league: "UFC"}) (and by extension ufc_card, all_slates, and recommended_bets for UFC) now surfaces the actual UFC card — currently the Gaethje/Topuria, Chandler/Ruffy, and Aswell/Bolanos fights. Before this fix, the same call returned an empty slate, making it look like the data feed was missing UFC events when it wasn't.

What's under the hood

  • screen_ranked was defaulting the focus book to the league preset's first preferred book (Pinnacle for most leagues). extractScreenRows then applied a focusPlays filter that dropped every row whose odds didn't include Pinnacle — which is every UFC row, since Pinnacle doesn't post UFC moneylines. Fix: only set the focus book when the user explicitly passes books; otherwise expand to all books in the payload.
  • Defensive follow-up: extractScreenRows now falls back to "all books in the row" if the requested focus book has no odds in that row — the focus-book feature was always meant as a best-effort preference, not a hard filter.
  • 1 new regression test in test/handler-integration.test.js asserts the fix against a fixture mirroring the live 2026-06-14 data shape (Pinnacle absent, BetOnline/Caesars/FanDuel/DraftKings present).

Honest framing

Even with the fix, the UFC card tonight still returns all TIER 4 / kaiCall: PASS — the system correctly identifies that the pregame moneylines don't have enough sharp-book consensus or line history to warrant a BET call. If you want signal on the card, try find_best_price for line shopping or wait until closer to tip when more line history accumulates.

Try it

Same as before. make install to upgrade, or npm install from a clean clone.

Then ask your agent: "Show me tonight's UFC card."


Full diff in CHANGELOG.md.

v2.1.1 — Fantasy Optimizer + tighter auth

Choose a tag to compare

@github-actions github-actions released this 14 Jun 20:07

v2.1.1 — Fantasy Optimizer + tighter auth

Fantasy picks land in the MCP, the spread-market bug from June 12 gets a permanent regression test, and the auth file that holds your PropProfessor cookies is now locked down to owner-only access.

What changed for users

  • Fantasy Optimizer tool — new fantasy_optimizer MCP tool for DFS-style fantasy picks. Requires a paid PropProfessor subscription with Fantasy Optimizer access. Query by league, fantasy app, market, or sport.
  • Auth file permissions tightenedpp-query login, installAuthFile, and the token cache now write 0o600 (owner-only) and chmod to enforce it on existing files. June 8 SEC-003 fix.
  • Spread-alias regression fixMARKET_ALIASES.spread and .handicap for NBA/WNBA/NCAAB/NCAAF/NFL/Soccer now correctly resolve to the per-league canonical name (e.g. "Point Spread" for NBA). Previously these markets returned empty payloads.

What's under the hood

  • New fantasy_optimizer tool in lib/propprofessor-fantasy-optimizer.js, wired through the standard handler pipeline.
  • lib/propprofessor-shared-utils.js now exports resolveMarketName(input, league) — the central alias resolver used by all market-aware tools.
  • pp-query login and installAuthFile write mode 0o600 on new and existing files (chmodSync for the existing-file path).
  • 19 new tests across fantasy optimizer, auth permissions, market aliases, and xurl player-name sanitization.

Honest framing (added 2026-06-14)

The spread-alias fix is correct for the alias resolution itself, but the underlying line-history tracking was incomplete in this release. The PropProfessor backend's /odds_history endpoint does not return a line field per entry, so even when the alias resolved correctly, the MCP couldn't track line movement on spread markets. v2.1.3 ships a defensive line-value backfill + degraded-data warning, and v2.1.4 hotfixes a bug in that warning check that was silently filtering out Puck Line / Run Line / Total rows. If you're using v2.1.1 with line-based markets, please upgrade to v2.1.4.


Full diff in CHANGELOG.md.

v2.0.1 — Pre-directory polish

Choose a tag to compare

@github-actions github-actions released this 12 Jun 00:49

v2.0.1 — Pre-directory polish

Docs-only patch. The README's v1.6.1 polish checklist (repo description, Mermaid diagram, FAQ, docs map, install path verification) was already comprehensive from prior cycles. v2.0.1 ships two targeted fixes for drift.

What changed for users

  • Nothing functional. Tool surface, algorithm, tier system unchanged.

What's under the hood

  • FAQ updated — TIER 1 hit rate sample size corrected from the v1.5.5-era 580-play count to the current 575-play backtest. Framing tightened to "sits around chance (~50%)" so the honest scope is the lead, not a footnote.
  • Status section updated — "Latest release" now names v2.0.0 specifically with a one-line description, so directory visitors landing on the README see the most recent release at a glance instead of clicking through.
  • Install path verified — server boots clean, NDJSON framing works end-to-end, initialize + tools/list returns all 23 tools, npm link --dry-run confirms pp-mcp / pp-query binaries would install.

Try it

Same as before.

Full diff in CHANGELOG.md