Skip to content

feat: metagame workflows, sideboard workflows, Moxfield search (#49, #50)#59

Merged
j4th merged 9 commits intomainfrom
feat/issues-49-50-v2.3.0
Apr 4, 2026
Merged

feat: metagame workflows, sideboard workflows, Moxfield search (#49, #50)#59
j4th merged 9 commits intomainfrom
feat/issues-49-50-v2.3.0

Conversation

@j4th
Copy link
Copy Markdown
Owner

@j4th j4th commented Apr 4, 2026

Summary

  • Issue Add constructed metagame workflow tools #49: 4 constructed metagame workflow tools (metagame_snapshot, archetype_decklist, archetype_comparison, format_entry_guide) with MTGGoldfish primary / Spicerack fallback
  • Issue Add sideboard analysis tools for Bo3 constructed formats #50: 3 sideboard workflow tools (suggest_sideboard, sideboard_guide, sideboard_matrix) with heuristic category matching and MTGGoldfish enrichment
  • Moxfield expansion: 2 new provider tools (search_decks, user_decks) + 3 new Pydantic models (MoxfieldDeckSummary, MoxfieldUser, MoxfieldSearchResult)
  • Shared utility: utils/fuzzy.py for archetype/matchup name matching (difflib.SequenceMatcher)
  • 2 new prompts: explore_format, build_constructed_deck

60 → 69 tools, 17 → 19 prompts. 1336 tests, 88% coverage.

Closes #49, closes #50.

Test plan

  • 1336 tests pass (mise run check)
  • Lint clean (ruff)
  • Typecheck clean (ty)
  • Live smoke test (mise run test:live)
  • Interactive smoke test (/smoke-test) for new metagame/sideboard/Moxfield tools

🤖 Generated with Claude Code

j4th and others added 8 commits April 3, 2026 20:13
#50

Add MTGGoldfish, Spicerack, and Moxfield clients to the workflow server
lifespan with feature-flagged initialization. Add _require_*() accessor
functions. Create utils/fuzzy.py for archetype name matching. Add 7 stub
tool wrappers (4 metagame, 3 sideboard) and 2 new prompts (explore_format,
build_constructed_deck). Update tool count 60→67, prompt count 17→19.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… search (#49, #50)

Phase 1 implementation across three parallel agents:
- Metagame workflows (metagame_snapshot, archetype_decklist, archetype_comparison, format_entry_guide)
- Sideboard workflows (suggest_sideboard, sideboard_guide, sideboard_matrix)
- Moxfield expansion (search_decks, user_decks provider tools + MoxfieldDeckSummary/User/SearchResult models)

69 tools, 19 prompts. 1336 tests passing, 88% coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
69 tools, 19 prompts, 1336 tests. Add metagame/sideboard/moxfield tools
to server instructions. Update ARCHITECTURE.md project structure and
CLAUDE.md implementation status.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ools

Update TOOL_DESIGN.md with 7 new workflow tools and 2 Moxfield tools.
Add Moxfield search/user caches to CACHING_DESIGN.md. Add metagame,
sideboard, and Moxfield search to smoke test skill (39→46 tests).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
archetype_decklist passed matched.slug (already format-prefixed from HTML)
to get_archetype(), which prepended the format again creating
"modern-modern-boros-energy". Fix: pass matched.name instead.

moxfield search_users sent "q" parameter but the API expects "filter",
causing HTTP 400. Fix: use correct parameter name.

Also updates smoke test skill username to a known-good Moxfield account.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deduplicate three separate archetype fuzzy-matching implementations
into the shared utils/fuzzy.py module. Add substring and word-overlap
passes so it handles partial names and reordered words. Replace inline
"4 Card Name" parsing in metagame.py with existing parse_decklist().
Cache card.name.lower() in sideboard suggest loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address all PR review findings:
- Replace 10x `except Exception` with `except ServiceError` in metagame
  and sideboard workflows per CLAUDE.md convention
- Fix archetype_comparison double-slug bug (arch.slug → arch.name)
- Add Pydantic Field validators (ge=0, ge=1, min_length=1) to Moxfield types
- Raise MoxfieldError on malformed _parse_search_result (consistent with _parse_deck)
- Add structlog warnings for silent fallbacks in moxfield.py
- Add 3 fuzzy matching tests (word overlap, substring, threshold boundary)
- Add test_uses_name_not_slug_for_get_archetype to archetype_comparison tests
- Fix test side_effects: RuntimeError/Exception → typed ServiceError subclasses
- Fix overly broad test assertion in sideboard guide
- Update TOOL_DESIGN.md: fix moxfield_search_decks/user_decks params, metagame_snapshot backends
- Fix workflow docstrings (lifespan backends list, archetype_decklist claims)
- Add sideboard_matrix to smoke test skill, update totals
- Sort __all__ in types.py, remove duplicate SpicerackStanding entry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@j4th j4th marked this pull request as ready for review April 4, 2026 04:30
@claude
Copy link
Copy Markdown

claude Bot commented Apr 4, 2026

Claude encountered an error —— View job


PR Review Summary

Verdict: APPROVE — solid implementation with two minor doc/test gaps worth addressing.


Important Issues (should fix)

1. docs/SERVICE_CONTRACTS.md not updated for new Moxfield endpoints (confidence: 88)

services/moxfield.py adds two new API paths — /v2/decks/search and /v2/users/search-sfw — but SERVICE_CONTRACTS.md has no entries for them. The PR Review Checklist in CLAUDE.md explicitly requires SERVICE_CONTRACTS.md updates when new endpoints are added. The existing ## Moxfield section documents only GET /v3/decks/all/{deck_id}.

Fix this →


2. Integration conftest missing respx mocks for new Moxfield endpoints (confidence: 82)

tests/integration/conftest.py mocks /v3/decks/.+ but not /v2/decks/search or /v2/users/search-sfw. The respx fixture uses strict mode (default), so any integration test that calls moxfield_search_decks or moxfield_user_decks via mcp_client would fail immediately with an unmatched route error. No tests currently call these tools through mcp_client, so nothing is broken today — but the gap blocks adding integration coverage later.

# tests/integration/conftest.py — add inside the respx.mock block
respx.get(url__regex=re.escape(MOXFIELD_BASE) + r"/v2/decks/search").mock(
    return_value=httpx.Response(
        200, json=_load_json(MOXFIELD_FIXTURES / "search_decks.json")
    )
)
respx.get(url__regex=re.escape(MOXFIELD_BASE) + r"/v2/users/search-sfw").mock(
    return_value=httpx.Response(
        200, json=_load_json(MOXFIELD_FIXTURES / "user_search.json")
    )
)

Fix this →


3. user_decks silently truncates to first page with no signal (confidence: 80)

providers/moxfield.py:348 calls client.search_decks(query=username, fmt=format) with the default page_size=20. For users with more than 20 decks the results are silently incomplete — the structured output omits total_results from the API response, so callers can't detect truncation. The tool docstring says "List a user's public decks" implying completeness.

Quick fix: expose result.total_results in structured output (note caveat that this counts all decks matching the query, not just this user's) and/or add a page_size parameter to the tool.

Fix this →


Completeness

Item Status
docs/TOOL_DESIGN.md ✅ Updated (metagame + sideboard + Moxfield tools)
docs/ARCHITECTURE.md ✅ Updated (tool counts, structure)
docs/CACHING_DESIGN.md ✅ Updated
docs/SERVICE_CONTRACTS.md ❌ Not updated for 2 new Moxfield endpoints
CLAUDE.md implementation status ✅ v2.3.0 entry added
.claude/skills/smoke-test/SKILL.md ✅ New sections for metagame/sideboard/Moxfield
tests/integration/conftest.py ⚠️ Missing mocks for /v2/decks/search and /v2/users/search-sfw
tests/integration/test_orchestrator_e2e.py ✅ Tool count (69), prompt count (19) updated
tests/test_parameter_descriptions.py ✅ Moxfield provider already in parametrize list
tests/conftest.py ✅ All 3 new Moxfield caches cleared
Unit tests ✅ All new service methods, provider tools, workflow functions, utils covered
Error path tests .is_error checks present in provider tests

What's Good

  • Graceful degradation: MTGGoldfish → Spicerack fallback in metagame_snapshot is well-structured; format_entry_guide degrades all the way down to "no metagame data" with format rules still returned
  • Fuzzy matching quality: 4-pass strategy in utils/fuzzy.py (exact slug → substring → word overlap → SequenceMatcher ratio) handles common user input variations well
  • Defensive Moxfield parsing: every level of _parse_deck, _parse_board, _parse_search_result uses isinstance + .get() — exactly right for an undocumented API
  • asyncio.gather(return_exceptions=True) in archetype_comparison for concurrent deck fetches with graceful per-archetype failure
  • B904 compliance: all raise ToolError(...) in except blocks use from exc
  • Cache key normalization: _moxfield_deck_key correctly normalizes URLs to IDs so URL vs. raw-ID calls share cache entries
  • Layer boundary: workflow function modules (metagame.py, sideboard.py) have zero MCP imports

@j4th
Copy link
Copy Markdown
Owner Author

j4th commented Apr 4, 2026

All three findings addressed:

  1. SERVICE_CONTRACTS.md — Added full Moxfield section documenting /v2/decks/search and /v2/users/search-sfw endpoints with connection details, parameters, response shapes, service architecture, and feature flag.

  2. Integration conftest — Added respx mocks for both new Moxfield endpoints (/v2/decks/search, /v2/users/search-sfw) using existing fixture files.

  3. user_decks truncation — Added total_results to structured output. When results are truncated, markdown now shows a note directing to moxfield_search_decks with pagination.

… signal

- Add Moxfield section to SERVICE_CONTRACTS.md documenting /v2/decks/search
  and /v2/users/search-sfw endpoints with response shapes
- Add respx mocks for both new Moxfield endpoints in integration conftest
- Expose total_results in user_decks structured output; add truncation
  note to markdown when results exceed page size

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@j4th j4th merged commit 3e7e956 into main Apr 4, 2026
7 checks passed
@j4th j4th deleted the feat/issues-49-50-v2.3.0 branch April 4, 2026 04:50
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.

Add sideboard analysis tools for Bo3 constructed formats Add constructed metagame workflow tools

1 participant