Skip to content

feat: league category posture in trade builder + finder#99

Merged
asecord92 merged 1 commit into
mainfrom
feat/league-posture-trades
Jul 22, 2026
Merged

feat: league category posture in trade builder + finder#99
asecord92 merged 1 commit into
mainfrom
feat/league-posture-trades

Conversation

@asecord92

Copy link
Copy Markdown
Owner

What & why

The trade tools only knew my weaknesses. Now they know where the other team stands, so:

  • Trade builder can reason about why an opponent wants your player and float realistic counters.
  • Trade finder becomes matchmaking — which owners genuinely need what you'd send back, vs. who's punting a category (their surplus).

Came out of a design conversation. The insight: ranks are volatile, roster construction is intent. A rank of "11th in SV" reads identically whether you're punting or desperate — so we pair the rank with a role count to disambiguate. My own team (1 closer, last in saves) correctly reads punt, not "go get a closer."

How

  • category_ranks now exposes compute_category_matrix — the full league matrix (per-team ranks + role counts). compute_category_ranks is a thin backward-compatible wrapper over it.
  • Role counts are free: parsed from roster_items[].position (raw Fantrax eligibility) + player_type. No new data, no migration. "SP,RP" is token-matched so multi-eligible arms count for both.
  • posture() is a pure, tested classifier (punt | need | strong | mid): weak rank + ≤1 contributor = punt; weak + real depth = need; top-third = strong. Never persisted — the heuristic tunes without a cache flush.
  • _run_category_ranks_compute writes both the legacy category_ranks blob and the new category_matrix row from one compute (no drift). Widget + rank history untouched.
  • Trade builder gets an opponent-posture line; finder gets rival postures for candidate owners. Prompt-facing surface is a tiny sliced digest (kept lean deliberately).

Notes / lean-by-design

  • All approximate (same caveat as existing category ranks) and framed that way to the model.
  • Old leagues that haven't re-synced simply won't have category_matrix yet → posture is silently omitted until their next sync/compute.
  • Parked (doable, not built): stashing each team's raw category values alongside ranks, to power a later "bunching / gap-to-next-rank" refinement.

Test

  • Pure posture / team_posture_summary / _role_counts / my_ranks_from_matrix exercised directly — punt vs. need behaves (incl. the 1-closer case).
  • ruff clean; engine + api.main import smoke pass.

🤖 Generated with Claude Code

Both trade tools now know where the *other* team stands, not just the
manager's own weaknesses — so counters explain why an opponent wants your
guy, and the finder becomes matchmaking (which owners need what you'd send).

- category_ranks now exposes compute_category_matrix: the full league matrix
  (per-team ranks + role counts), the reusable primitive. compute_category_ranks
  is a thin backward-compatible wrapper over it.
- Role counts come free from roster_items[].position (raw Fantrax eligibility) +
  player_type — no new data, no migration. SP/RP token-matched so "SP,RP" counts both.
- posture() is a pure, tested classifier (punt|need|strong|mid): weak rank + <=1
  contributor = punt (deliberate zero, e.g. 1 RP and last in SV); weak + depth = need.
  Never persisted, so the heuristic tunes without a cache flush.
- _run_category_ranks_compute writes both the legacy category_ranks blob and the
  new category_matrix row from one compute (no drift); widget/history untouched.
- build_trade_context adds the opponent's posture line; build_finder_context adds
  rival postures for candidate owners. Prompt-facing view is a tiny sliced digest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dynastyos Ready Ready Preview, Comment Jul 22, 2026 4:38pm

@railway-app
railway-app Bot temporarily deployed to dynastyops-secordlabs / dynastyos-pr-99 July 22, 2026 16:38 Destroyed
@asecord92
asecord92 merged commit 6542afe into main Jul 22, 2026
5 checks passed
@asecord92
asecord92 deleted the feat/league-posture-trades branch July 22, 2026 16:47
asecord92 added a commit that referenced this pull request Jul 22, 2026
The posture PR (#99) wrote a new category_matrix row to dashboard_cache, but
the table's widget CHECK constraint didn't whitelist it — every compute raised
23514 and, because both rows were upserted together, took the essential
category_ranks + history write down with it. Category ranks silently stopped
updating (and start/sit/news lost their weak-category context line).

- Migration 20260722_category_matrix_widget.sql adds category_matrix to the
  allowed widget list (apply manually in Supabase SQL editor).
- Split _run_category_ranks_compute into two writes: category_ranks + history
  land first and unconditionally; category_matrix is a separate best-effort
  upsert, so a matrix failure can never block the essential ranks again.

Co-authored-by: Claude Opus 4.8 <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