Skip to content

AUR-382 + AUR-393: Phase 3 complexity refactors + raidbots provider CLI - #32

Merged
aurokin merged 19 commits into
mainfrom
aur-382-phase3-complexity-refactors
May 31, 2026
Merged

AUR-382 + AUR-393: Phase 3 complexity refactors + raidbots provider CLI#32
aurokin merged 19 commits into
mainfrom
aur-382-phase3-complexity-refactors

Conversation

@aurokin

@aurokin aurokin commented May 31, 2026

Copy link
Copy Markdown
Owner

Closes AUR-382 (phase 3 complexity refactors) and AUR-393 (Raidbots report consumption + local SimC handoff).

This PR started as AUR-382 only; the AUR-393 raidbots branch was merged into it so both ship together. Two sections below, combined verification at the bottom.

AUR-382 — Phase 3 complexity refactors (behavior-preserving)

Helper/module extraction to clear every Radon E/F hotspot and the named D targets. No output shapes change.

  • 1 warcraft_content.article_bundle: query_article_bundle F53→A, compare_article_bundles F43→A.
  • 2–3 wrapper main.py: _guide_builds_simc_payload F77→C, guide_compare_query E35→C.
  • 4 wowhead_cli.citation_pack: citation_pack_from_entity E39→A, citation_pack_from_compare E38→A (differential byte-identical; compare snapshot test added).
  • 5 warcraftlogs main.py: five D-rated analytics payload/resolver helpers → ≤C.
  • 6 simc build_input.py + modify_build_command D30→B6; swap-decode abort regression test.
  • 7 (optional) raiderio/wowprogress: no D-rated handlers → N/A.

radon cc -n E packages/ is now empty. Remaining D functions left intentionally (behavior-sensitive, not analytics payloads): warcraftlogs auth_pkce_login, simc validate_talent_transport_command.

AUR-393 — Raidbots provider CLI

New raidbots-cli (raidbots … directly or warcraft raidbots …). Consumes completed, public Raidbots reports and hands off to local simc; no submission automation (no sanctioned API) and no cross-provider import.

  • Commands: doctor, inspect-report <url-or-id> (--no-raw), input <url-or-id>, explain-input (--text/--file/stdin).
  • Kind-aware json2 parsing (quick-sim actor + metrics vs ranked profilesets for Top Gear/Droptimizer); freshness/citations/scope.
  • SimC handoff emits suggested local simc commands (shlex-quoted); the classifier recognizes both deathknight/death_knight class forms, trailing # comments, split class/spec/hero_talents, and space-padded assignments.
  • SSRF-safe report-id resolution that round-trips env-overridable report URLs; env-overridable base/path templates.
  • Wired into the root warcraft dist (hatch packages), wrapper capabilities (search/resolve not_supported, retail expansion), .importlinter, scripts/dev_deploy.sh, and make test-live.

See docs/raidbots/README.md.

Combined verification

  • make check green: 970 passed, 237 deselected; ruff + mypy + import-linter all clean.
  • The merge is the exact union of both branches' file lists; a slice-4 differential confirms byte-identical compare output vs. main.
  • diffwarden (codex-gpt54 / codex-gpt55 / cursor-composer) over the combined base:main diff, iterated to convergence: codex-gpt55 + cursor-composer clean. Every valid finding fixed with a regression test — deploy/live-test wiring, underscore class tokens, trailing # comments, split talents, empty-list profilesets, space-padded talents/copy, env-override URL round-trip. False positives (root editable install; compare unique_by_entity drop) were refuted by execution and annotated inline; one pathological /{id} URL-template edge was declined by design (guard documented inline). A scoped, execution-based classifier audit surfaced the split-talents gap proactively.

🤖 Generated with Claude Code

aurokin and others added 7 commits May 31, 2026 11:25
Extract behavior-preserving helpers to clear the two F-rated hotspots in
warcraft_content/article_bundle.py:

- query_article_bundle (F 53 -> A): per-kind haystack builders plus a generic
  `_collect_kind_matches`; section title-filter becomes a predicate closure.
  `results_by_kind` insertion order preserved (compact emit is order-sensitive).
- compare_article_bundles (F 43 -> A): `_collect_bundle_evidence` single-pass
  accumulator plus `_build_analysis_surface_rows` / `_build_section_evidence_rows`
  / `_build_build_reference_rows`. Membership key-type asymmetry (build keyed by
  "::".join, others raw) and the identity-representative pick are kept verbatim.

No output-shape change. radon: no E/F (or D) functions remain in the module.
make check green (938 passed).

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Behavior-preserving extraction of the two wrapper main.py E/F hotspots
(same provider family, same file):

- _guide_builds_simc_payload (F 77 -> C 11): split the per-build loop body into
  _build_simc_handoff_row (owns temp-packet create + try/finally unlink),
  with _resolve_handoff_build_code, _build_handoff_transport_packet,
  _invoke_simc_for_handoff, _handoff_evidence_section, _handoff_simc_section,
  _count_simc_handoff_successes, and _handoff_citations.
- guide_compare_query (E 35 -> C 15): extract the per-provider loop body into
  _process_guide_compare_provider returning (provider_row, bundle_input|None),
  with _resolve_guide_compare_candidate for the resolve+search-fallback step.
  The loop still iterates selected_providers in order, appending provider_row
  always and bundle_input when present (order into compare_article_bundles and
  provider_results preserved).

No output-shape change. radon: no E/F (or D) functions remain in wrapper main.py.
make check green (938 passed).

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Clear the last two E-rated hotspots in wowhead_cli/citation_pack.py with
behavior-preserving section collectors that keep delegating to _add_source /
_add_claim (so the build_citation_pack sort+dedup winner and the #-fragment URL
rewrite are unchanged):

- citation_pack_from_entity (E 39 -> A): _entity_page_url +
  _collect_entity_page_and_citation_sources / _collect_tooltip_summary_claims /
  _collect_linked_entity_citations / _collect_comment_citations.
- citation_pack_from_compare (E 38 -> A): _merge_entity_citation_packs /
  _collect_comparison_linked_entity_sources / _collect_comparison_field_claims.

Verified byte-identical to the prior implementation via a differential check over
entity edge cases and a full compare payload. Adds a snapshot test for
citation_pack_from_compare (previously untested) covering the comment-key
collision and #-fragment anchor rewrite.

radon: no E/F (or D) functions remain in the module; full repo now has zero E/F.
make check green (939 passed).

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Behavior-preserving extraction of the five D-rated payload/resolver helpers in
warcraftlogs_cli/main.py (follows the #31 boss_kills extraction style):

- _resolve_encounter (D 25 -> A 5): split into _resolve_encounter_by_id /
  _resolve_encounter_by_name dispatch.
- _encounter_ranking_row_payload (D 26 -> C 12): collapse the repeated
  first-non-empty/first-int `next(...)` scans into _first_non_empty_str / _first_int.
- _boss_spec_usage_payload (D 22 -> A 4): extract _accumulate_boss_spec_counts.
- _comp_samples_payload (D 26 -> A 4): extract _accumulate_comp_presence plus
  _record_comp_class_presence / _record_comp_signature (avoids relocating the
  hotspot into the accumulator).
- _aura_compare_rows (D 22 -> B 10): extract the per-key _compare_row builder.

No output-shape change. The only remaining D in the module is the pre-existing
auth_pkce_login OAuth flow (out of scope for this analytics-payload slice).
make check green (939 passed).

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Behavior-preserving extraction of the simc D-rated build hotspots:

build_input.py (all three D-rated functions cleared):
- extract_build_spec_from_packet (D 28 -> A): one helper per transport form
  (_packet_spec_from_wowhead_ref / _wow_export / _split_talents), tried in the
  original short-circuit order so only the matching branch mutates source_notes.
- extract_build_spec_from_text (D 23 -> A): _single_line_build_spec (keeps the
  detect()-set source_kind on the export path) + _parse_simc_build_text_lines.
- identify_build (D 29 -> low): _direct_build_identity + _probe_build_matches.

main.py:
- modify_build_command (D 30 -> B 6): _resolve_modify_tree_entries /
  _build_modify_overrides / _assemble_modified_spec / _modify_build_diff_payload.
  modifications-list order and the no-modifications failure are preserved (_fail
  raises typer.Exit). Adds the BuildResolution import.

No output-shape change. make check green (939 passed). The remaining simc main.py
D is validate_talent_transport_command (separate command, outside this slice).

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address a diffwarden finding (false positive) that read the extracted
_resolve_modify_tree_entries as continuing after a swap-source decode failure.
_fail raises typer.Exit, so the command aborts exactly as before; the post-_fail
returns are unreachable defensive code (now noted inline).

Adds a regression test proving a failing --swap-*-tree-from source aborts with
decode_failed and emits no modified build even when --add is also present.

make check green (940 passed).

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mark phase-3 complexity refactors complete (repo has zero E/F functions), note the
two deliberately-deferred behavior-sensitive D functions (auth_pkce_login,
validate_talent_transport_command), and record the behavior-preserving extraction
work under CHANGELOG [Unreleased].

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

linear Bot commented May 31, 2026

Copy link
Copy Markdown

AUR-382

AUR-393

aurokin and others added 6 commits May 31, 2026 12:46
…R-393)

New `raidbots-cli` package that consumes public Raidbots reports and bridges
their SimC input to the local `simc` tool. No submission automation (no
sanctioned Raidbots API) and no cross-provider import — handoff is by emitted
commands, matching the wrapper's existing guide->simc design.

Commands: doctor; inspect-report <url-or-id> (--no-raw); input <url-or-id>;
explain-input (--text/--file/stdin). Reports are parsed from SimC json2
(quick-sim actor + metrics, or ranked profilesets for Top Gear/Droptimizer)
with freshness/citations/scope; the handoff emits ready-to-paste input plus
suggested local simc commands (shell-quoted, class/spec-resolved).

Wired into the wrapper (provider registry + raidbots passthrough), import
contracts (root + independence + layers), root scripts/hatch/mypy_path,
conftest src paths + live-env map, packaged at the workspace version 0.3.0.
Network/transport and parse failures map to the shared error envelope; report
references normalize to an ID and fetch via the configured (env-overridable)
base+templates, and the cache key includes the resolved URL.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mark the Raidbots "First Useful Slice" as shipped: a commands section in
docs/raidbots/README.md (doctor / inspect-report / input / explain-input,
handoff posture, env-overridable URLs, deferred submission), promote it from
"planned" in the docs index, and add consumer-facing routing to the warcraft
skill (provider synopsis row, Read Next entry, references/raidbots.md).

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mark Tier 1/2 and the First Useful Slice as shipped (linking the authoritative
Commands section), and note the local-vs-report crosswalk stays deferred — the
no-import handoff covers the common case. Keeps the doc consistent with actual
CLI behavior.

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

Combine the two independent feature branches so the PR carries both the
behavior-preserving complexity refactors and the new raidbots provider CLI.
Branches were siblings off main with non-overlapping changes (raidbots package
+ wrapper wiring vs. helper extractions); .gitignore/CHANGELOG additions are
disjoint.

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

Post-merge fixes surfaced by the combined-PR diffwarden review and merge-readiness audit:

- scripts/dev_deploy.sh: add `raidbots` to BIN_NAMES so `make dev-deploy` links the
  console script alongside the other eight providers (it was the only one omitted).
- Makefile: add RAIDBOTS_LIVE_TESTS=1 to LIVE_TEST_ENV so `make test-live` actually
  reaches the raidbots live tests instead of silently skipping them on the env gate.
- raidbots simc_input: the suggested `simc describe-build` command now carries a
  `requires` hint — unlike decode-build it needs a default/explicit APL and otherwise
  fails not_found when no SimC repo is checked out. Add a regression test asserting the
  hint is present on describe-build and absent on the self-sufficient decode-build.

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

A combined-PR diffwarden run flagged (2 reviewers, P2) that
`_collect_comparison_linked_entity_sources` "drops unique_by_entity URLs"
because the visible loop only iterates the single-element ("shared_items",)
tuple. It does not: the dict-shaped unique_by_entity bucket is collected by
the loop immediately below. Verified by differential execution — the slice-4
extraction's compare output is byte-identical to the pre-refactor builder on a
payload populating both shared_items and unique_by_entity, and the existing
compare snapshot test already asserts the unique_by_entity keys.

Add an inline comment documenting the two-shape split so future reviews don't
re-fixate on the apparent (refuted) drop. No behavior change.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aurokin aurokin changed the title AUR-382: Phase 3 complexity refactors (clear all E/F hotspots) AUR-382 + AUR-393: Phase 3 complexity refactors + raidbots provider CLI May 31, 2026
aurokin and others added 6 commits May 31, 2026 13:45
…tract

From a second combined-PR diffwarden run (two single-reviewer P2s, both verified):

- Fix (codex-gpt55, valid): SimC accepts both the addon/profile actor form
  (`deathknight=`) and the documented manual-creation keyword form
  (`death_knight=`/`demon_hunter=`). `_find_actor` only matched the former, so a
  report whose SimC input used the underscore form was misclassified as `advanced`
  and dropped the decode/describe handoff. Normalize the class token (strip
  non-alphanumerics, mirroring simc-cli's _normalize_actor_class) before the class
  check and emit the canonical no-underscore form. Verified against SimC's own
  midnight-branch profiles (which use `deathknight=`) and the upstream class-keyword
  docs. Added a regression test for both DK and DH underscore tokens.

- Decline (codex-gpt54, by-design): `resolve_report_id` pins URL-*input* parsing to
  the documented `/report/{ID}` shape and always accepts a bare ID. Env overrides
  adapt the *fetch* URLs to live drift with no code change; a bare ID is drift-proof.
  Sharpened the inline comment to state this distinction so future reviews don't
  re-fixate. No behavior change.

make check green (963 passed, 237 deselected; ruff/mypy/import-linter clean).

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Final-round diffwarden finding (codex-gpt54, conf 0.94, verified valid):
`_iter_clean_lines` dropped comment-only lines but preserved trailing `# ...`
comments, so `mage="Main" # actor` missed the actor entirely (the actor regex
anchors on end-of-line) and `spec=frost # note` / `talents=CYG # build` carried
the comment into the suggested decode/describe commands. SimC treats `#` as a
comment delimiter anywhere on a line; strip it like simc-cli's build-text parser
(build_input.py:330,370). Added a regression test over a fully commented profile.

make check green (964 passed, 237 deselected).

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

A scoped execution-based classifier audit (5 construct categories, adversarially
verified) surfaced one confirmed realistic in-scope gap: profiles whose talents are
expressed via the split keys (class_talents=/spec_talents=/hero_talents=) with no
combined talents= line yielded talents_present=False, so simc_handoff emitted only
`simc sim -` and suppressed the decode/describe handoff — even though actor_class and
spec were extracted. simc-cli treats this form as first-class (simc_split_talents),
and simc decode-build/describe-build already accept --class-talents/--spec-talents/
--hero-talents (verified via source + runtime --help).

Recognize the split keys: classify_simc_input now reports talents_present for the
split form, and simc_handoff prefers the combined talents= line but falls back to the
split flags (each shlex.quote'd) so the decode/describe commands still fire. Combined
form still wins when both are present (canonical addon-export shape). Added regression
tests for the split-only and combined+split cases.

make check green (966 passed, 237 deselected).

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

Three verified findings from the latest combined-PR diffwarden run (codex-gpt54;
codex-gpt55 + cursor-composer clean):

- URL round-trip (conf 0.96): doctor/citations build report URLs from the env-overridable
  RAIDBOTS_REPORT_PATH_TEMPLATE, but resolve_report_id only extracted IDs from the literal
  `/report/` shape, so an overridden template emitted URLs the CLI's own input parser would
  reject. resolve_report_id now derives the extraction prefix from the configured template
  (single source of truth), keeping the `/report/{ID}` and bare-ID fallbacks and the
  host-ignoring SSRF invariant. _resolve_report_id_or_fail threads the configured template.

- Profileset presence (conf 0.91): `_has_profilesets` keyed on non-emptiness, so a bare
  `"profilesets": []` fell through to the quick-sim path and reported players[0] (the
  multi-profile baseline template) as a user actor — contradicting the function's own intent.
  Now routes on container presence; quick sims never emit the key. Added an empty-list test.

- Space-padded assignments (conf 0.93): talents detection used exact `talents=`/`class_talents=`
  prefixes while _scalar_assignments and _find_actor already tolerate whitespace around `=`, so
  `talents = CYG` silently dropped the decode/describe handoff. talents/_split_talents now split
  on the first `=` and strip the key; talents_present derives from the parsed assignments.

make check green (969 passed, 237 deselected; ruff/mypy/import-linter clean).

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

Follow-on to the previous space-padding fix (codex-gpt54, conf 0.97): copy_count still
used an exact `copy=` prefix while talents/actor/assignments tolerate whitespace, so
`copy = alt` (valid SimC) misclassified a multi-profile run as quick_sim/advanced. Count
copy directives via the same split-on-first-`=` + stripped-key rule (guarded on `=`
presence so a bare `copy` line is not counted). Completes the classifier's space-tolerance.

make check green (970 passed, 237 deselected).

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

diffwarden (codex-gpt54) flagged that a `RAIDBOTS_REPORT_PATH_TEMPLATE='/{id}'`
root-level override would stop URL round-tripping because resolve_report_id only
uses the template prefix when it is longer than "/". Declined: that guard is
deliberate — a root/empty prefix would mis-extract the first path segment of any
URL, and reports are not served at a root-level `/{id}` on Raidbots. Documented the
rationale inline so the guard isn't relaxed into a mis-extraction regression. No
behavior change. Bare IDs always resolve regardless of template.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aurokin
aurokin merged commit 9b6e920 into main May 31, 2026
2 checks passed
@aurokin
aurokin deleted the aur-382-phase3-complexity-refactors branch May 31, 2026 20:34
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