Skip to content

Backlog sweep (12 issues): wrapper parity, WCL analytics, Blizzard + CurseForge providers, identity, decision docs - #36

Merged
aurokin merged 19 commits into
mainfrom
aur-backlog-sweep
Jun 3, 2026
Merged

Backlog sweep (12 issues): wrapper parity, WCL analytics, Blizzard + CurseForge providers, identity, decision docs#36
aurokin merged 19 commits into
mainfrom
aur-backlog-sweep

Conversation

@aurokin

@aurokin aurokin commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Summary

Autonomous backlog sweep of 12 Linear issues (AUR-384–499) plus a progressive-disclosure
documentation pass, across 13 commits. Each issue followed the same loop: read issue + linked
issues + code → plan → independent plan review → implement → make check → diffwarden (default
reviewer set) until clean → commit/push → Linear → docs. Declined diffwarden findings carry inline
code comments explaining the invariant/tradeoff.

make check is green on the tip: ruff + mypy + import-linter (boundaries kept) + 1082 tests
passed
.

Issues shipped

Issue Theme
AUR-456 Declare wrapper provider deps + warcraftlogs-cli pyproject
AUR-384 Wrapper capability parity lock-down + WCL discovery-hint routing
AUR-496 Relax none-expansion passthrough + Phase-4 expansion policy
AUR-386 Guide evidence freshness metadata + SimC-handoff anchor
AUR-387 WCL character-rankings trust block + spec-kill-samples cohort
AUR-388 WCL finished-report caching (finish-state TTL) + derived-output trust metadata
AUR-391 Raider.IO season-aware leaderboard + explicit resolved_season
AUR-392 WowProgress guild-snapshot + history-trajectory (+ two-word-class fix)
AUR-455 Blizzard Game Data + Profile reads + live OAuth client-credentials
AUR-458 Additive identity normalization across 5 provider surfaces
AUR-395 Provider candidate decision docs (CurseForge GO; Undermine/RaidPlan DEFER)
AUR-499 CurseForge provider scaffold (doctor + addon lookup)

Plus: a documentation refactor completing the warcraft skill's progressive-disclosure index —
added consumer-facing references/blizzard-api.md + references/curseforge.md and wired both into
SKILL.md so all 11 registered providers are disclosed.

Notable design decisions

  • New providers ship with provenance.verified: false. No live Blizzard credentials or
    CurseForge API key were available to run the one-time endpoint-confirmation spikes, so
    blizzard-api (AUR-455) and curseforge (AUR-499) follow documented API conventions but mark
    every payload unverified. *_LIVE_TESTS-gated tests confirm them once credentials exist:
    BLIZZARD_LIVE_TESTS=1 / CURSEFORGE_LIVE_TESTS=1.
  • CurseForge robustness: numeric ids are validated to be WoW projects (gameId); slug lookups
    match the exact slug client-side (with a cross-game gameId guard) so an ignored/renamed filter
    param can never bind the wrong mod; changelog is best-effort with an explicit {file_id, error}
    marker, never a silent null and never fatal.
  • WCL finished-report caching keys TTL on finish state via the existing ttl_override; a live
    report is never stored under the 24h finished TTL. cache_provenance describes the report's
    finish state (documented caveat for the bounded live→finished window) — see
    docs/warcraftlogs/CACHING.md.
  • AUR-395 is decision-only; the CurseForge GO scaffold is AUR-499. Stale doc indexes were
    reconciled (architecture/README, docs/README, WRAPPER_PROVIDER_CONTRACT, IDENTITY_CONTRACT,
    ROADMAP).

Verification

  • make check green (lint + typecheck + import boundaries + 1082 fast tests).
  • Per-issue diffwarden runs (default reviewer set) iterated to clean; declined findings have inline
    rationale comments. Provider CLIs remain intentionally outside mypy files= (repo convention;
    documented in pyproject.toml).
  • New fixture-backed contract tests for blizzard-api and curseforge, plus *_LIVE_TESTS-gated
    live contract tests that skip when unconfigured.

Reviewer notes

  • Provider count moved 10 → 11; the exact none-expansion / fanout-exclusion sets were updated across
    test_warcraft_wrapper.py, test_provider_contract.py, and the live wrapper tests.
  • CHANGELOG [Unreleased] records every user-visible change.

🤖 Generated with Claude Code

aurokin and others added 19 commits June 1, 2026 16:47
The warcraft-cli wrapper 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.

- Declare the 4 missing standalone provider distributions in
  warcraft-cli/pyproject.toml (raiderio-cli, simc-cli, warcraft-wiki-cli,
  wowprogress-cli) and sort the dependency list.
- Give warcraftlogs-cli its first pyproject.toml (was source-only). It
  declares simc-cli to formalize the existing import-linter-sanctioned
  warcraftlogs_cli.main -> simc_cli.talent_transport edge, plus its
  import-derived deps (typer, httpx, warcraft-{core,api,content}-cli).
- Add warcraftlogs-cli to the wrapper deps now that it has a distribution.
- Add tests/test_warcraft_cli_packaging.py: static ast+tomllib guard that
  every provider app imported by the wrapper is a declared dep, so the gap
  cannot silently reappear. Verified it fails pre-fix, passes post-fix.

Relocating the shared talent-transport symbol to drop the provider->provider
dep is tracked in AUR-501.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ting

Routing/doctor/registration are already shipped; this slice locks them down
with regression tests and refreshes the contract docs. No routing-heuristic
change (the alpha-only report-code contract is preserved).

- Add a registry-vs-CLI capability parity test: for every registered provider,
  wrapper_capabilities[search|resolve] must equal that CLI's own
  doctor.capabilities[surface], and a bare-`ready` surface may never sit over a
  CLI reporting coming_soon/not_supported/ready_explicit_report_only. Scoped to
  search/resolve (only warcraftlogs/simc/blizzard-api emit a `doctor` key).
- Add regression tests proving `warcraft search`/`resolve` on a non-report
  warcraftlogs query keep WCL in the fanout as ready_explicit_report_only,
  return the structured discovery hint (count:0, message, supported_inputs,
  suggested_commands), and never surface a fabricated resolved match.
- Docs: refresh WRAPPER_PROVIDER_CONTRACT.md WCL "Current state" line + add the
  previously-missing raidbots and blizzard-api entries; align docs/warcraft/README.md.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Today `warcraft --expansion <key> <none-provider> ...` (simc, blizzard-api)
fails with exit 1 unsupported_provider_expansion. A none-expansion provider has
no expansion semantics to honor, so relax it to passthrough with an advisory.

- New _expansion_passthrough_advisory: none-mode + requested expansion returns
  an advisory; fixed/profiled mismatch still hard-errors (genuine mismatch).
- New _run_passthrough centralizes all 10 proxy commands. For the relax case it
  captures both provider streams, attaches an additive advisory to the JSON
  payload (expansion_filter + expansion_advisory block), preserves the payload
  verbatim, and surfaces the advisory on stderr when output is non-JSON.
- Fix latent _invoke_sub_app bug: under standalone_mode=False click *returns* a
  typer.Exit code instead of raising, silently swallowing provider failures to
  exit 0; now re-raised so passthrough propagates non-zero exits (both paths).
- Author "Phase 4 Expansion Policy" in EXPANSION_FILTERING.md: provider-promotion
  criteria, wrapper-key -> WCL site-profile mapping table, none-expansion
  passthrough rule. Added missing raidbots/blizzard-api matrix rows.
- Tests: wrapper relax (blizzard + simc doctor), fixed-mismatch error, no-advisory
  baseline; contract none-mode reason == provider_has_no_expansion_support.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Metadata-consistency closeout for the guide comparison/handoff surfaces.

- warcraft_content.write_article_bundle stamps an ISO-8601 UTC `exported_at`
  into the manifest (method/icy-veins/warcraft-wiki; export_version unchanged);
  _bundle_descriptor carries it through to comparisons.
- _guide_build_handoff_freshness single-bundle path reads exported_at, so
  `guide-builds-simc <bundle>` now reports freshness.status=known instead of
  unknown.
- New shared _guide_comparison_packet adds a top-level `freshness` rollup and a
  `comparison_evidence` block (compared bundle count, providers, matching rules,
  per-bundle freshness) to BOTH guide-compare and guide-compare-query, preserving
  comparison_scope + citations.bundle_paths. New --max-age-hours (default 24).
- Orchestration export rows source exported_at from the bundle manifest so reuse
  freshness and comparison freshness read one timestamp.
- Older timestamp-less bundles degrade gracefully (no export_version bump).
- Tests: article-bundle manifest exported_at; method/icy-veins/wiki export
  manifest exported_at; guide-compare + guide-compare-query freshness/evidence;
  guide-builds-simc known-from-bundle. Docs: USAGE.md + CHANGELOG.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an additive `trust` block to `warcraftlogs character-rankings`
(ranking_basis, resolved scope echo, reused sampled freshness, and a
source-character class_spec_identity) alongside the preserved `raw`
passthrough. Built from existing identity/freshness helpers only; WCL
exposes class as an internal classID enum so the class name is not
fabricated (spec-only normalization).

Add `warcraftlogs spec-kill-samples`: a new sampled cross-report
primitive that hardens the existing `--spec-name` participant filter into
an explicit, labeled cohort (requires --spec-name + boss scope). Reuses
collect_boss_kill_rows; emits sample_size (full matching cohort),
matching_participant_count, exclusion/truncation counts, freshness, and
citations. Because the shared sampler orders kills by ascending duration,
the truncated head is flagged with truncation_order +
not-a-representative-sample note (SAFE_ANALYTICS_RULES: no silent caps).
Explicitly not a spec ranking leaderboard.

Register in ALL_COMMANDS + canonical envelope key spec_kill_samples
(legacy `kills` dual-emitted/deprecated); document in PAYLOAD_KEYS.md,
README, SCOPING, LIVE_MATRIX; add unit + envelope + gated live tests and a
matrix case. CHANGELOG updated.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Key Warcraft Logs report cache TTL on finish state. Finished reports
(endTime > 0) cache under the new WARCRAFTLOGS_FINISHED_REPORT_CACHE_TTL_SECONDS
(default 24h, added to CacheTTLConfig); live reports keep the 60s TTL. The TTL
is resolved at the cache-write site (both client and user GraphQL endpoints) via
a finish-state resolver, so a live report is never stored under the finished TTL.

Report rankings (population-relative percentiles that keep changing) and report
listings stay on the short TTL even when finished; the finished TTL applies only
to immutable report-detail payloads (fights/events/tables/graphs/master-data/
player-details + the report metadata lookup). Added report-level endTime to the
report-detail queries so the resolver can read finish state.

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. In cache-off
deployments (WARCRAFTLOGS_CACHE_BACKEND=none) the emitted cache_ttl_seconds is
null rather than claiming a TTL that is never applied.

New caching contract in docs/warcraftlogs/CACHING.md (linked from the README,
which condenses the two prior aspirational sections); CHANGELOG updated.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `raiderio leaderboard mythic-plus`, a thin season-scoped view over the
existing sampled-run primitive. Emits 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`. It paginates
to satisfy `--limit` (deriving pages from the 20-runs-per-page Raider.IO page
size, capped at 10 pages) and emits a `sample` block (requested_limit,
returned_run_count, pages_requested, pages_fetched, limit_reached) so a short
provider response is explicit, never a silent cap.

Every Mythic+ analytics command (standalone `mythic-plus-runs` + the
sample/distribution/threshold families) now additionally surfaces
`resolved_season` in its `query` block alongside the existing `season` key.
`--season current` (or empty) resolves to the Raider.IO current default season:
the request omits the season param and the effective slug is recovered from the
API response, instead of forwarding the literal "current".

`raiderio doctor` reports `mythic_plus_leaderboard: ready`. README command
surface updated and the stale "Suggested Next Commands" examples reconciled to
shipped command shapes. Unit tests (incl. pagination + season-current + empty
degrade) and a RAIDERIO_LIVE_TESTS-gated live contract test. CHANGELOG updated.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… fix

Add two provider-local analytics commands and fix a progression difficulty
token bug:

- guild-snapshot: derives current guild state (progression, item-level rank
  context, per-tier rank series) from a single guild-history traversal, with
  no extra guild-page fetch in the command layer. fetch_guild_history now
  carries the main page's current_item_level/current_encounters so the
  current state survives an empty/incomplete history series.
- history-trajectory: oldest-to-newest tier timeline with delta_vs_previous
  rank/item-level movement. Deltas compare consecutive tiers (different
  raids/difficulties), so they are emitted as descriptive movement with
  explicit caveat notes, not a normalized metric.
- Fix client.py difficulty token to use _progress_snapshot()["difficulty"]
  (token "M") instead of the "N/N (M)" summary string.
- doctor reports guild_snapshot/history_trajectory capabilities as ready.
- Freshness emits cache_ttl_seconds: null when caching is disabled.
- Docs: USAGE.md, docs/wowprogress/README.md, skills reference, CHANGELOG.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the AUR-390 scaffold with a client-credentials OAuth token flow
and three typed read commands.

- New blizzard_api_cli/client.py: client-credentials OAuth (ported from
  warcraftlogs) — token fetched once and cached in shared state under
  blizzard-api-client-credentials, keyed by sha256(region, id, secret),
  region-scoped in-memory + cross-process reuse until ~60s before expiry.
- Commands: `blizzard realm <slug>` (dynamic), `blizzard item <id>` (static),
  `blizzard character <realm> <name>` (profile, retail only). Region/namespace
  routing via --region (default BLIZZARD_REGION, else us; us/eu/kr/tw/cn with
  aliases) and --game-version retail|classic (or --classic shorthand); --locale
  passes through. Realm/character slugs are lowercased.
- Envelope: {ok, provider, command, kind, query, provenance, data} on success;
  {ok:false, ..., error:{code, message}} + nonzero exit (never a traceback) for
  missing_client_credentials, unsupported_region, unsupported_game_version,
  classic_profile_unsupported, http_error, network_error, invalid_response.
- doctor flips game_data/profile to ready, surfaces the region/routing block and
  the client-credentials token-cache state.

HONESTY: the endpoint hosts, OAuth token URL, and namespace strings follow
documented Blizzard API conventions but have NOT been confirmed against live
endpoints in this environment (no credentials available to run the spike).
doctor carries region.verification and every command payload carries
provenance.verified=false. A human must run
`BLIZZARD_LIVE_TESTS=1 pytest -q -m live tests/test_blizzard_api_live.py` with
real credentials to confirm them (CN endpoints + classic namespaces least
certain).

Tests: fixture-backed contract tests (monkeypatched request_with_retries),
updated doctor test, gated live tests. Docs: blizzard-api README,
AUTH_ARCHITECTURE phase 3, CONTRACT_TEST_CATALOG, CHANGELOG.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap raw class/spec/encounter/ability data in the shared warcraft_core.identity
contract on the remaining provider surfaces. Purely additive — raw fields are
preserved alongside each new sibling identity block.

- raiderio: class_spec_identity on ranking roster rows, resolve
  character-profile candidates, character search results (class-only), and
  guild roster_preview rows. high confidence only when class+spec resolve.
- raidbots: class_spec_identity on inspect-report actor summaries from the sim
  report's class/specialization (high when both present, gated on non-whitespace
  content; unknown when both missing).
- wowprogress: class_spec_identity on the character payload (normalized,
  confidence none; spec sourced from the SimDPS table), and encounter_identity
  on guild encounter kill rows (name-only -> normalized, encounter_id null).
  Also fixes a pre-existing parser bug where two-word classes (Death Knight,
  Demon Hunter) were truncated to the trailing token in class_name/race.
- method + icy-veins: ability_identity on Wowhead spell linked-entity rows
  (canonical, spell_id set); other entity types unchanged.

Docs: IDENTITY_CONTRACT coverage matrix + notes updated; CHANGELOG. Tests:
resolved + degrade pairs per provider, guild-no-identity + non-spell-unchanged
guards.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…aidPlan DEFER)

Rewrite the three later-provider research stubs into decision docs:
- CurseForge = GO: Decision section with gate-referencing rationale, an
  Expected User Workflow (addon lookup by slug/id -> metadata + files +
  changelog) marked approved-but-not-yet-implemented, required wrapper
  capabilities, expansion_mode=none, and a link to the AUR-499 scaffold.
- Undermine Exchange = DEFER and RaidPlan = DEFER, each with a concrete,
  checkable un-gate condition so the next revisit is itself AFK-able.

Fix stale doc indexes to match shipped providers and the decisions:
- architecture/README.md: drop shipped blizzard-api/raidbots from the
  docs-only list; relabel as candidate decision docs.
- README.md: replace generic (planned) tags with GO/DEFER candidate tags;
  blizzard-api no longer tagged planned.
- WRAPPER_PROVIDER_CONTRACT.md: blizzard-api entry updated from scaffold/
  coming_soon to shipped Game Data + Profile (AUR-455).
- IDENTITY_CONTRACT.md: blizzard-api note no longer implies the Game Data/
  Profile endpoints are pending; identity wiring is separately deferred.
- ROADMAP.md: record the AUR-395 decisions in its row and add an AUR-499
  scaffold row.

Decision-only deliverable; no code changed. make check green (1059 passed).

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stand up a read-only `curseforge` provider, mirroring the blizzard-api
scaffold (the GO decision from AUR-395):

- New packages/curseforge-cli: auth.py (static x-api-key discovery via
  .env.local -> provider env -> process env), client.py (api.curseforge.com,
  slug/id resolution with client-side exact-slug + gameId guards, metadata +
  latest files + best-effort changelog), main.py (doctor + addon commands).
- addon returns {metadata, latest_files, changelog} with the standard
  {ok, provider, command, kind, query, provenance, data} envelope; errors
  (missing_api_key, addon_not_found, http_error, network_error,
  invalid_response) emit a clean ok:false envelope, never a traceback.
- Changelog is best-effort: a failed fetch (HTTP, network, or malformed body)
  degrades to an explicit {file_id, error} marker; it never fails the lookup
  and is never silently reported as "no changelog".
- Host/endpoints/response shapes follow the documented public CurseForge Core
  API but are pending one-time live confirmation (provenance.verified=false;
  CURSEFORGE_LIVE_TESTS=1).

Wiring: root pyproject (script + wheel + mypy_path), .importlinter (3
contracts), warcraft-cli dep, providers.py registration (expansion_mode=none,
doctor/addon ready, search/resolve coming_soon), wrapper passthrough command,
conftest sys.path, dev_deploy.sh BIN_NAMES. Bumped provider-count and the
exact none-expansion / fanout exclusion sets across the wrapper + provider
contract tests.

Tests: fixture-backed contract suite (slug/id resolution, exact-slug + cross-
game + gameId guards, all error codes, best-effort changelog markers), doctor +
auth discovery, wrapper routing, and a CURSEFORGE_LIVE_TESTS-gated live test.
make check green (1082 passed). Docs + CHANGELOG updated; CurseForge graduated
from candidate decision doc to shipped scaffold.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…, curseforge)

Progressive-disclosure pass: the `warcraft` skill is a concise SKILL.md router
plus per-provider references loaded on demand, but its index covered only 9
providers and omitted blizzard-api (AUR-455) and curseforge (AUR-499).

- Add consumer-facing references/blizzard-api.md and references/curseforge.md
  (Best For / Start With / Auth / Effective Use / Boundaries), matching the
  existing reference style; each notes its auth requirement and the
  verified:false trust posture so agents treat unconfirmed results as
  best-effort.
- Wire both into SKILL.md's Provider Synopsis table and Read Next index, so the
  harness now discloses all 11 registered providers.

The standalone-skill generator (scripts/generate_provider_skills.py) is a
curated subset and intentionally untouched here; references/*.md + SKILL.md are
the consumer source of truth.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Branch-vs-main diffwarden flagged that the new spec-kill-samples
command (AUR-387) was absent from warcraftlogs doctor's capabilities
map, so doctor-as-source-of-truth no longer matched the command set.

Add the spec_kill_samples capability alongside its sampled-analytics
siblings (top_kills/boss_spec_usage/comp_samples), assert it in the
doctor phase-one capabilities test to prevent regression, and note the
doctor surface in the AUR-387 CHANGELOG entry (matching the AUR-392
"<provider> doctor reports the new ... capabilities" convention).

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e-to-null)

Branch-vs-main diffwarden flagged that an empty-but-present changelog
(file exists, endpoint returns data:null) emits {file_id, source_url,
body: null} rather than a top-level null, arguing callers keying off
`changelog is None` will misclassify it.

The recommended fix (return null) is declined: it would degrade the
deliberate three-state design and contradict the already-tested
error-marker behavior. top-level null is reserved for "no file to
fetch"; a failed fetch returns {file_id, error}; so a successful-but-
empty fetch returning a {file_id, source_url, body: null} object (more
provenance than a failure, not less) is the consistent choice and
preserves which file was checked + its source url.

The real defect was contract clarity, so this:
- adds an inline invariant comment at the body:null return so future
  review runs do not re-fixate;
- makes docs/curseforge/README.md and skills/.../references/curseforge.md
  precise and consistent (null only when no files; detect empty notes via
  changelog.body, not `changelog is null`); the old "when a file exposes
  none it is null" wording was the misleading bit;
- adds a contract test locking in the empty-body object form.

No code behavior change; output shape unchanged from what AUR-499 shipped.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Branch-vs-main diffwarden (final pass) surfaced three valid P2s in the
new provider work; all verified against the code and fixed:

1. Blizzard live suite gated by the wrong env var. test_blizzard_api_live.py
   was added (and documented as the BLIZZARD_LIVE_TESTS path) but never
   registered in conftest LIVE_TEST_ENV_BY_FILE, so the collection hook fell
   back to WOWHEAD_LIVE_TESTS and `BLIZZARD_LIVE_TESTS=1 pytest ...` silently
   skipped. Register it; add a wrapper meta-test asserting every
   test_<provider>_live.py declares its own flag so this can't recur.

2. `--classic` silently overrode an explicit `--game-version retail`.
   resolve_game_version only treated --classic as conflicting for
   *unsupported* versions, so `--game-version retail --classic` routed to
   classic with no error. Reject any explicit non-classic --game-version
   paired with --classic (--classic alone leaves game_version=None, so the
   shorthand still works). Contract test added.

3. CurseForge numeric lookups labeled non-WoW/malformed records as WoW.
   The gameId gate only rejected a *present int* != 1, so an absent or
   non-int gameId still returned ok:true with provenance.game_id hardcoded
   to 1. Require a present int gameId (invalid_response otherwise), matching
   the non-string-changelog schema-drift posture. Contract test added.

CHANGELOG: corrected the AUR-499 changelog-null wording to match the
clarified contract, and noted the AUR-455 contradictory-flag rejection.

make check green (1086 passed).

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…te freshness

Default-set diffwarden loop (iter 2) flagged the `or _iso_now_utc()`
fallback in _process_guide_compare_provider as stamping legacy
timestamp-less bundles as freshly exported, so a stale bundle could be
reused without a real anchor.

Declined as a false positive after tracing the full data flow: all three
guide-compare providers stamp exported_at on export (wowhead via
_guide_export_manifest; method/icy-veins via write_article_bundle), and
the fallback lives in the export path (guide-export just re-wrote the
bundle), so "now" reflects a real just-happened export. A timestamp-less
bundle is also never reused: the reuse gate requires freshness "fresh"
and _guide_compare_freshness(None) is always "stale", forcing a
re-export. Strengthened the inline comment so future runs don't re-fixate.

No code behavior change.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ot a regression

Default-set diffwarden loop (iter 4) flagged the relax-to-passthrough
StringIO capture as buffering the whole provider output in memory and
losing streaming for large simc/report workflows.

Declined: the buffering only runs on the explicit `warcraft --expansion
<key> <none-provider> ...` combination (normal `warcraft <provider> ...`
returns at the advisory-is-None branch and streams untouched), and there
is no incremental streaming to lose anyway — provider commands emit a
single JSON envelope all at once, and attaching the expansion advisory as
an additive sibling key requires the whole payload to parse it. The
buffering is intrinsic to the annotate-the-payload feature (AUR-496:
"relax is never silent"), not a regression of normal workflows.
Strengthened the inline comment so future runs don't re-fixate.

No code behavior change.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ge search/resolve

Default-set diffwarden loop (iter 5) flagged that curseforge advertises
search/resolve as coming_soon in doctor but doesn't define those
commands, so `curseforge search dbm` returns Click's "No such command"
(exit 2) instead of the structured envelope callers expect for
not-yet-ready surfaces. Verified valid: simc already defines coming_soon
search/resolve stubs (the repo precedent), and BOTH new providers
(curseforge AND blizzard-api) had the identical gap.

Fix both: define search/resolve as structured coming_soon stubs (exit 0,
{ok, provider, command, kind: coming_soon, coming_soon: true,
suggested_command, ...}) mirroring simc. Capabilities stay coming_soon,
so the wrapper still excludes them from search/resolve fanout
(provider_supports_surface unchanged) — the stubs only fix direct and
`warcraft <provider> ...` passthrough probes.

Added per-provider contract tests plus a cross-provider wrapper meta-test
asserting any surface a provider's doctor advertises as coming_soon emits
a structured stub (exit 0), never a Click error — so this can't recur for
a future provider. CHANGELOG notes the stub behavior for both providers.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aurokin
aurokin merged commit cea6a27 into main Jun 3, 2026
2 checks passed
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