You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Lorrgs provider: new no-auth lorrgs command for the public Lorrgs API (https://api2.lorrgs.io/api/*) after a user requested support for lorrgs.io. Ships doctor; static metadata commands (roles, classes, specs, spec, spec-spells, zones, zone, zone-bosses, bosses, boss, boss-spells, spell, trinkets); season metadata commands (current-season, season <slug>); ranking commands spec-ranking <spec-slug> <boss-slug> and spec-ranking-info <spec-slug> <boss-slug> for top-parse cooldown timelines; comp-ranking <boss-slug> with repeatable --role/--spec filters and kill-time bounds; search / resolve for Lorrgs ranking URLs, Warcraft Logs report URLs, bare mixed-alphanumeric report codes, and free text spec/boss queries; report-overview <report-id-or-url> for report metadata without queueing fight/player timeline work; and cached-only user-report / user-report-fights reads. Browser-driving the public Lorrgs page showed the UI preserves type=damage-done on /user_reports/<id>/fights, so user-report-fights now preserves type from report URLs and accepts --type. Each command emits {ok, provider, command, kind, query, provenance, data} on success, with provenance carrying the exact source_url, API host, site URL, and upstream source posture (warcraftlogs, Wowhead tooltips), and {ok:false, ..., error:{code, message}} with a nonzero exit (never a traceback) for not_found, invalid_query, rate_limited, http_error, network_error, and invalid_response. Routed through the wrapper as warcraft lorrgs ... with search/resolve ready, expansion_mode=fixed, and supported_expansions=["retail"]. The CLI intentionally does not expose Lorrgs queued load/dirty endpoints. Live public API coverage is gated by LORRGS_LIVE_TESTS=1 pytest -q -m live tests/test_lorrgs_live.py, including the user-provided Warcraft Logs URL https://www.warcraftlogs.com/reports/bG3xDYPqKjLm8XaR?fight=22&type=damage-done. See docs/lorrgs/README.md.
Wrapper cooldown evidence packet: new warcraft cooldown-packet <warcraftlogs-report-url-or-code> --actor-id <source-id> --phase <n> composes cached Lorrgs report fight data (phase markers, boss casts, boss/spec slugs, player source ids), Lorrgs spell metadata and optional top-parse spec-ranking samples, and Warcraft Logs report-events --data-type casts for exact player cast timestamps. The packet emits selected phase windows, player cooldown casts in the phase, boss casts, tracked spell metadata, top-parse comparison samples, source commands/provenance, and caveats. It is intentionally evidence-only, not a synthesized cooldown recommendation.
CurseForge provider scaffold (AUR-499): new curseforge command for the public CurseForge addon API, decided GO in AUR-395. First slice ships curseforge doctor (install state, api_key auth posture for CURSEFORGE_API_KEY discovered via .env.local → ~/.config/warcraft/providers/curseforge.env → environment, and capability metadata — doctor/addon ready, search/resolvecoming_soon) and curseforge addon <slug-or-id> — resolves the addon (numeric → mod id, else a gameId=1 slug search) and returns data.metadata (raw CurseForge mod record), data.latest_files (the mod's latestFiles), and data.changelog (the newest file's changelog, best-effort: top-level null only when the addon has no files; otherwise an object with the file's body — null when that file exposes no notes — plus source_url, or a {file_id, error} marker on a failed fetch, never failing the lookup). Each command emits {ok, provider, command, kind, query, provenance, data} on success — provenance carries game_id/mod_id/slug/resolved_by/source_urls — and {ok:false, ..., error:{code, message}} with a nonzero exit (never a traceback) for missing_api_key, addon_not_found, http_error, network_error, and invalid_response. Routed through the wrapper as warcraft curseforge … with expansion_mode=none (addon game-version compatibility lives inside file records, not the wrapper's expansion axis). The host, endpoints, and response shapes follow the documented public CurseForge Core API but are pending one-time live confirmation — every command payload carries provenance.verified: false; run CURSEFORGE_LIVE_TESTS=1 pytest -q -m live tests/test_curseforge_live.py with a real CURSEFORGE_API_KEY to confirm them. See docs/curseforge/README.md. Deferred: dependency graph / game-version compatibility / file-download workflows. search/resolve are not implemented yet but exist as structured coming_soon stubs (a JSON envelope with coming_soon: true and a suggested_command, not a Click "No such command" error), matching the surface doctor advertises.
Additive identity normalization across provider surfaces (AUR-458): raw fields preserved; each surface gains a sibling identity block from the shared warcraft_core.identity contract. raiderio — class_spec_identity on ranking roster rows, resolve character-profile candidates, character search results (class-only normalized), and guild roster_preview rows (high confidence only when both class+spec resolve, else none). raidbots — class_spec_identity on inspect-report actor summaries from the sim report's class/specialization (high when both present, unknown when both missing). wowprogress — class_spec_identity on the character payload (status normalized, confidence: none; spec sourced from the SimDPS table when present), and encounter_identity on guild encounter kill rows (name-only → normalized, encounter_id: null). method + icy-veins — ability_identity on Wowhead spell linked-entity rows (status canonical, spell_id set); other entity types unchanged. docs/foundation/IDENTITY_CONTRACT.md coverage matrix + notes updated.
Blizzard API Game Data + Profile reads + live OAuth (AUR-455): completes the AUR-390 scaffold with a client-credentials OAuth token flow (ported from warcraftlogs: token fetched once and cached in shared state under blizzard-api-client-credentials, keyed by sha256(region, id, secret), reused until ~60s before expiry) and three typed read commands — blizzard realm <slug> (dynamic), blizzard item <id> (static), and blizzard character <realm> <name> (profile, retail only). Region/namespace routing is honored: --region (default BLIZZARD_REGION, else us; us/eu/kr/tw/cn with aliases like na), and --game-version retail|classic (or the --classic shorthand; passing --classic alongside a contradictory explicit --game-version is rejected with unsupported_game_version rather than silently routing to classic) selects the namespace-class infix; --locale passes through. Each command emits {ok, provider, command, kind, query, provenance, data} on success — provenance carries region/namespace/namespace_class/game_version/source_url — and {ok:false, ..., error:{code, message}} with a nonzero exit (never a traceback) for missing_client_credentials, unsupported_region, unsupported_game_version, classic_profile_unsupported, http_error, network_error, and invalid_response. blizzard doctor flips game_data/profile to ready and surfaces the region/routing block. search/resolve remain coming_soon but exist as structured stubs (a JSON envelope with coming_soon: true and a suggested_command, not a Click "No such command" error), matching the surface doctor advertises. The endpoint hosts, OAuth token URL, and namespace strings follow documented Blizzard API conventions but are pending one-time live confirmation — doctor carries this marker under region.verification and every command payload carries provenance.verified: false; run BLIZZARD_LIVE_TESTS=1 pytest -q -m live tests/test_blizzard_api_live.py with real credentials to confirm them (CN endpoints and classic namespace strings are least certain). See docs/blizzard-api/README.md and docs/architecture/AUTH_ARCHITECTURE.md (phase 3).
WowProgress guild-snapshot + history-trajectory (AUR-392): new wowprogress guild-snapshot <region> <realm> <name> composes current progress, world/region/realm ranks, item level, and encounter summary with a per-tier rank series into one provider-local normalized payload (kind: guild_snapshot) plus citations and freshness — built from a single guild-history traversal (no extra guild-page fetch in the command layer). New wowprogress history-trajectory <region> <realm> <name> (kind: history_trajectory) emits per-tier rows ordered oldest→newest with tier-over-tier delta_vs_previous (world/region/realm rank change with an explicit improved flag + item-level change), each row carrying its page_url, plus caveats that each row is a final-for-tier snapshot (not live) and that consecutive tiers are different raids/difficulties (descriptive movement, not a normalized metric). wowprogress doctor reports the new guild_snapshot/history_trajectory capabilities.
Raider.IO season-aware leaderboard + explicit resolved_season (AUR-391): new raiderio leaderboard mythic-plus command — a thin, season-scoped, single-page view over the existing sampled-run primitive emitting kind: mythic_plus_leaderboard, a query block with the explicit resolved_season, run rows, sampled freshness (sampled_at + cache_ttl_seconds), and citations.leaderboard_urls. Every Mythic+ analytics command (mythic-plus-runs, sample/distribution/threshold families) now additionally surfaces resolved_season in its query block alongside the existing season key, and --season current (or empty) now resolves to the Raider.IO current default season (the request omits the season param and the effective slug is recovered from the API response) rather than forwarding the literal current. raiderio doctor reports the new mythic_plus_leaderboard: ready capability. README command surface + stale "Suggested Next Commands" examples reconciled to shipped shapes.
Warcraft Logs finished-report caching + derived-output trust metadata (AUR-388): report cache TTL is now keyed on finish state — finished reports (endTime > 0) cache under the new WARCRAFTLOGS_FINISHED_REPORT_CACHE_TTL_SECONDS (default 24h), live reports keep the 60s TTL, and a live report is never stored under the finished TTL (resolved at the cache-write site on both the client and user GraphQL endpoints). Sampled cross-report commands (boss-kills, top-kills, spec-kill-samples, kill-time-distribution, boss-spec-usage, comp-samples, ability-usage-summary) now emit cache_provenance ({finished, live, cache_ttl_seconds, source}), a populated integer freshness.cache_ttl_seconds (was null), and a consolidated sample_scope (ranking_basis + filters + returned/excluded/truncated). Report-encounter commands gain the same cache_provenance block alongside their existing stability block. Report rankings and report listings keep the short TTL even when the report is finished (rankings are population-relative percentiles that keep changing); the finished TTL applies only to immutable report-detail payloads. In cache-disabled deployments (WARCRAFTLOGS_CACHE_BACKEND=none) the emitted cache_provenance/freshnesscache_ttl_seconds is null rather than claiming a TTL that is never applied. New caching contract documented in docs/warcraftlogs/CACHING.md (linked from the README, which condenses the two prior aspirational sections).
Warcraft Logs spec-kill-samples command (AUR-387): a new sampled cross-report primitive that hardens the existing --spec-name participant filter into an explicit, labeled cohort. It requires --spec-name (alongside boss scope) and returns kills that contained at least one participant of that spec, with kind: spec_filtered_kill_samples, cohort: spec_filtered_participant_kill_cohort, ranking_basis: spec_filtered_participant_kill_samples, and a sample block carrying sample_size, matching_participant_count, exclusion/truncation counts, freshness, and citations. It is explicitly not a spec ranking leaderboard. Canonical envelope key spec_kill_samples (legacy kills dual-emitted + deprecated), registered in ALL_COMMANDS and documented in docs/warcraftlogs/PAYLOAD_KEYS.md, with a LIVE_MATRIX row + gated live contract test. warcraftlogs doctor reports the new spec_kill_samples capability alongside its sampled-analytics siblings.
Warcraft Logs character-rankings trust block (AUR-387): character-rankings now emits an additive trust block alongside the preserved raw passthrough — ranking_basis: public_character_zone_rankings, a resolved scope echo (zone/difficulty/metric/partition/size), reused sampled freshness, and a source-character class_spec_identity (spec-only normalization; WCL exposes class only as an internal classID enum, so the class name is not fabricated). Built from existing identity/freshness helpers only.
Log actor → profile handoff (AUR-385): new warcraft actor-profile <report-code> <actor-name> cross-walks a Warcraft Logs report actor to a Raider.IO character profile. It resolves the actor from report-player-details (optionally narrowed with --fight-id, --allow-unlisted for unlisted reports), looks up the Raider.IO profile by region/realm/name (with a --region override when the log omits region), and emits both class_spec_identity blocks with an agree/conflict reconciliation. Ambiguous actor names (same name on different realms, or multiple class/specs across fights) are rejected with guidance rather than silently resolved. The join is a soft match on region + realm + name — explicitly not a canonical cross-provider actor id. Also documents the provider × identity coverage matrix and the demonstrated handoffs in docs/foundation/IDENTITY_CONTRACT.md.
Raider.IO normalized class/spec identity (AUR-385): raiderio character now emits an additive class_spec_identity block (shared warcraft_core.identity contract) alongside the raw class_name/active_spec_name strings, normalizing the character's class and active spec for cross-provider handoffs (e.g. a Warcraft Logs report actor → Raider.IO profile). Raw source fields are preserved; identity status is normalized when class/spec resolve and degrades to unknown when they are absent.
Blizzard API provider scaffold (AUR-390): new blizzard command for the official Battle.net WoW API. First slice ships blizzard doctor only — reports install state, OAuth client-credentials auth posture (BLIZZARD_CLIENT_ID/BLIZZARD_CLIENT_SECRET discovery via .env.local → ~/.config/warcraft/providers/blizzard-api.env → environment, redacted state from warcraft_core.auth), a deferred region/namespace block (surfaces BLIZZARD_REGION if set), and honest capability metadata (doctor ready; search/resolve/game_data/profile coming soon). Registered in the wrapper as warcraft blizzard … with expansion_mode=none (deferred) until endpoint routing lands. Game Data/Profile endpoints, live auth validation, and region/namespace routing are deferred to a follow-up. See docs/blizzard-api/README.md and docs/architecture/AUTH_ARCHITECTURE.md (phase 3).
Raidbots provider CLI (AUR-393): new raidbots command for report consumption and local SimC handoff. raidbots doctor, raidbots inspect-report <url-or-id> (--no-raw), raidbots input <url-or-id>, and raidbots explain-input (--text/--file/stdin). Parses SimC json2 reports (quick-sim actor + metrics, or ranked profilesets for Top Gear/Droptimizer), emits freshness/citations/scope, and produces ready-to-paste SimC input with suggested local simc commands. No submission automation (no sanctioned Raidbots API) and no cross-provider import — handoff is by emitted commands. Routed through the wrapper as warcraft raidbots …; report URLs are env-overridable (RAIDBOTS_BASE_URL, RAIDBOTS_*_PATH_TEMPLATE). See docs/raidbots/README.md.
Developer tooling (AUR-366): make check, make test-fast, CI non-live unit tests, optional .pre-commit-config.yaml, make benchmark-cache, make fixture-refresh-hints, and architecture docs for contract fixtures (CONTRACT_TEST_CATALOG.md, FIXTURE_MAINTENANCE.md).
Wowhead additive normalization for item on entity and entity-page: schema_version, normalized.item with per-field provenance (docs/wowhead/NORMALIZATION.md).
Wowhead URL expansion detection: detect_expansion_from_url, --url on entity / entity-page, auto-detect on search when --expansion is omitted, expansion-detect command, and expansion_url_policy in wowhead doctor.
Shared output ergonomics in warcraft_core.output: field projection, --fields-strict, --compact-max-chars, output profiles (agent|human|debug), and DiagnosticsCollector for diagnostics metadata.
Wowhead global flags: --profile, --fields-strict, and --compact-max-chars (wired through the shared output layer).
Wowhead compare --preset gear|quest|spell presets for field diffs, link limits, and comment sampling defaults.
Wowhead --citation-pack and shared warcraft_core.citations builder for deterministic source URLs and per-claim anchors.
Wowhead linked-graph command for depth-limited linked-entity traversal with --relation type filters.
Wowhead comments intelligence: date/author/keyword/reply filters plus --insights for freshness, near-duplicate detection, and cited deterministic insights.
GitHub Actions CI (.github/workflows/ci.yml) runs make lint, make typecheck, make lint-boundaries, and make test-fast on pull requests and pushes to main.
Warcraft Logs live command matrix: tests/fixtures/live_matrix.py, tests/test_live_command_matrix.py, and make test-live-matrix (see docs/warcraftlogs/LIVE_MATRIX.md).
Wrapper expansion policy review: warcraft-wiki is fixed to retail (phase 3 complete; simc remains deferred).
Wowhead request efficiency: per-command session HTTP dedupe, --stream JSONL output for large arrays, and --max-concurrency for parallel comment reply hydration.
Import boundary checks via import-linter (make lint-boundaries, CI job) enforcing provider isolation and core layering.
warcraftlogs graphql now provides a raw official GraphQL passthrough with literal/file/stdin query input, JSON variables, declared-variable scoping helpers, explicit endpoint selection, opt-in caching, introspection, and the same graphql_warnings envelope used by typed Warcraft Logs commands.
report-encounter-buffs now returns a typed buffs.preview payload with per-row source (or target when --view-by target), aura (with identity contract), and reported buff-table fields (reported_total_uptime, reported_total_uses, reported_bands), parallel to report-encounter-casts. New --preview-limit flag (default 20, max 200) bounds the preview length; buffs.total and buffs.preview_truncated mirror the casts contract so agents can compose follow-ups against either sibling command without learning two output shapes.
Changed
Guide evidence metadata consistency (AUR-386): warcraft_content.write_article_bundle now stamps an ISO-8601 UTC exported_at into the manifest (method/icy-veins/warcraft-wiki bundles; export_version unchanged), giving downstream wrapper handoffs a freshness anchor. warcraft guide-builds-simc <single-bundle> now reports freshness.status: known (anchored on exported_at) instead of unknown. warcraft guide-compare now emits a top-level freshness rollup and a comparison_evidence block (compared bundle count, providers, matching rules, per-bundle freshness) with a new --max-age-hours flag (default 24), preserving the existing comparison_scope and citations.bundle_paths. Older timestamp-less bundles degrade gracefully (no export_version bump).
none-expansion passthrough (AUR-496): warcraft --expansion <key> <provider> ... for a provider with expansion_mode=none (simc, blizzard-api) now relaxes to passthrough with an advisory note instead of failing with exit 1 unsupported_provider_expansion. The provider runs unchanged and its payload is preserved verbatim with an additive expansion_filter: "passthrough_no_expansion_semantics" and an expansion_advisory block. fixed/profiled providers asked for an unsupported expansion still hard-fail (genuine mismatch). Authored the "Phase 4 Expansion Policy" section in docs/architecture/EXPANSION_FILTERING.md (provider-promotion criteria, wrapper-key → WCL site-profile mapping table, and the none-expansion passthrough rule).
Wrapper routing/doctor/registration lock-down (AUR-384): added a registry-vs-CLI capability parity test so the wrapper can never advertise a search/resolve surface more capable than the provider CLI's own doctor reports, across all 10 providers. Added regression tests proving warcraft search/resolve on a non-report warcraftlogs query keeps the provider in the fanout (ready_explicit_report_only), returns the structured discovery hint (count: 0, message, supported_inputs, suggested_commands), and never surfaces a fabricated resolved match. No routing-heuristic change. Refreshed docs/foundation/WRAPPER_PROVIDER_CONTRACT.md (WCL discovery-hint behavior + previously-missing raidbots and blizzard-api entries) and docs/warcraft/README.md.
Complexity (AUR-382, behavior-preserving): cleared every Radon E/F hotspot and the named D targets via helper extraction — warcraft_content.article_bundle query/compare, the wrapper guide-compare-query and guide→simc handoff handlers, wowhead_cli.citation_pack builders, five warcraftlogs analytics payload helpers, and the simc build_input / modify-build paths. No output shapes changed. Added a citation_pack_from_compare snapshot test and a modify-build swap-decode abort regression test. radon cc -n E packages/ is now empty.
Warcraft Logs: extract sampled boss-kill analytics into boss_kills.py, report_payloads.py, and sampling_utils.py; collect_boss_kill_rows complexity E (32) → A (5).
Full-repo Ruff cleanup: make lint now covers packages/, tests/, and scripts/ (Typer B008 allowed on provider main.py; long fixture strings in tests use E501 ignore). make lint-all is an alias.
Complexity: split validate_talent_transport_packet validation into focused helpers in warcraft_core.identity.
WowProgress CLI: split the monolithic main.py into context, identity, search, and analytics modules; main.py now holds Typer wiring and command handlers only.
Warcraft Logs typed commands emit a canonical command-based top-level envelope key (for example boss_kills, report_encounter_buffs). Legacy primary keys remain dual-emitted for one minor with deprecated_keys metadata. See docs/warcraftlogs/PAYLOAD_KEYS.md.
Architecture docs use progressive disclosure: active reference under docs/architecture/, completed milestones under docs/architecture/history/. Open engineering work moved to Linear (Warcraft CLI project).
simc repo resolution no longer falls back to a machine-specific default path; unset installs use the managed checkout path with source: unset until simc checkout or --set-root.
Fixed
WowProgress guild-history raw rows now carry the parsed difficulty token (e.g. M) instead of the full 8/8 (M) progress summary string (AUR-392); the normalized tiers rows were already correct.
Wrapper packaging gap (AUR-456): warcraft-cli imports 10 provider apps at module load but declared only 5 as runtime deps, so a standalone pip install warcraft-cli (outside the warcraft umbrella wheel) would ModuleNotFoundError. Declared the missing distributions (raiderio-cli, simc-cli, warcraft-wiki-cli, wowprogress-cli, warcraftlogs-cli) and added a static packaging-consistency test (tests/test_warcraft_cli_packaging.py) so the gap cannot silently reappear. Also gave warcraftlogs-cli its first pyproject.toml (it was source-only); it declares simc-cli to formalize the existing import-linter-sanctioned warcraftlogs_cli.main -> simc_cli.talent_transport edge (relocating that shared symbol to drop the provider→provider dep is a follow-up).
Review remediation: WowProgress WowProgressClient re-export on main (runtime import from context).
Wowhead --stream emits a JSONL header when row arrays are empty; session JSON cache returns deep copies on fresh fetches.
Wowhead citation pack avoids doubling URL fragments when comment anchors already include #.
Output shaping only attaches diagnostics when --profile debug (not merely when a collector has values).
Warcraft Logs live matrix: required --ability-id, aura compare windows, and --actor-id for player talents; per-test skips instead of module-wide fixture skips.
Live Wowhead CI matrix uses precise pytest -k filters so classic does not match mop-classic.
Removed
PR review helper scripts and make review-comments / make request-pr-reviews (session-only workflow; use gh pr comment directly when needed).