feat(classes): structured leveling kill loops, sourced stat priorities, one talent-table shape - #2
Merged
Merged
Conversation
… talent-table shape
Four data changes, no site code.
1. R11 — new structured/classes/<class>/leveling.yaml for all nine classes:
spec verdict (favored / alternatives / avoid), opener chain, moment-to-moment
priority, mob-selection rule, weapon rule, stat priority and an explicit gaps
list. Every field carries {url, author, authority, date} citations in the same
shape the playbook YAML uses; all 391 citations (195 distinct posts) were
checked against structured/forum/posts for author, date and authority tier.
2. R12 — talents.leveling_order filled where a source gives an order:
hunter/beast-mastery-ranged-dps (17 steps), rogue/subtlety-melee-dps (11),
shaman/enhancement-melee-dps (22), druid/balance-ranged-dps (3 approximate
anchors), and warrior/arms-melee-dps' existing order gained its approximate
levels (~14 … ~40). Bracket and derived levels are written as strings ("~14",
"10–33") and flagged with leveling_order_approximate.
3. Talent-order tables in all nine leveling.md files converged on one shape:
| Levels | Talent | Note |, one row per step. Mage's four prose runs and
druid's prose anchors became tables; shaman's, warlock's and paladin's packed
bracket cells were split into one row per step; shaman's offline
talent-calculator Link column was dropped (codes kept once, in prose below the
table); rogue's five-column and hunter's/priest's header variants were
normalised. No recommendation changed and every citation stayed on its row.
4. stat_priority curated from the sources for 32 more playbooks (5 → 37 of 47).
The remaining 10 are left absent on purpose: for those specs the playbook's own
stat section says the sources give no order (rogue subtlety and PvP specs,
druid feral-tank and restoration-healer, the mage/paladin/priest PvP specs).
Each entry carries stat_priority_note with the caps, contested points and
authority the order rests on.
site/src/data regenerated. python3 scrape/validate.py: 309 files, 55,686
citations, 0 unresolved. From site/: build-data.py, npm run check (contract OK)
and npm run smoke (78 routes, 0 failures) all pass.
structured/classes/<class>/leveling.yaml is not read by site/scripts/build-data.py
yet — emitting it is the presentation branch's step.
…he site navigation changes
…clarify parse contract
…aman talent links
…fix link coverage claims
…t-link coverage counts
…estore tank qualifiers
…r leveling page and the glossary work # Conflicts: # site/src/data/meta.json
…ty order and prose
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
The captain's original ask, in his words: "the data on the turtle-knowledge-base- is extensive, the github page that was created is yet too hard to digest and not user friendly enough for players who want to learn about their class and how to play it best with which rotation, levelling etc. you name it."
On the ranked proposal that followed: "I am following the recommendations and approving to continue", then "Approved but also do the others".
Most recently, asked whether to ship a thin guide-hero strip now or write the missing data first, he chose: "3 then 1" - write the data first, then build the presentation against data that actually supports it. THIS BRANCH IS THE "3": data only, no site code. Another branch is standing by, paused, waiting on what this produces.
Context: this repository archives the Turtle WoW community - official site, phpBB forums in English and German, the Fandom wiki - captured 2026-09-10 before the forums closed. The servers are offline; the last patch was 1.18.1. synthesis/classes/** holds per-class, per-spec-role playbooks plus leveling and gear guides. structured/classes/** holds the machine-readable per-spec YAML the site reads. Every claim in both cites a forum post, wiki page or staff statement, per synthesis/CONVENTIONS.md.
The captain's ask resolves to four pieces of source and structured data (recommendations R11 and R12 of a completed site-usability investigation, plus two gaps later review rounds exposed). The site cannot show players what it does not have: only 5 of 47 spec guides carried a stat_priority, so a gearing tile was blank on the other 42; two classes wrote their talent orders as prose rather than tables, so 7 spec guides showed no level-by-level order; and no class had a structured "what do I actually press while leveling" - that advice existed only as quotes buried in prose, which is exactly why a level-1 player needed seven screens of scrolling to find it.
R11 - a structured leveling kill loop per class: create structured/classes//leveling.yaml for all nine classes, extracted from the Playstyle and equivalent sections of synthesis/classes//leveling.md - the spec verdict for leveling, the opener chain, the moment-to-moment priority, the mob-selection rule, the weapon rule, and the stat priority. Every field carries its citation in the same shape the existing structured YAML uses. This is what turns "what do I press at level 1" into a zero-scroll answer the way the level-60 rotation already is.
R12 - talents.leveling_order in the favored leveling spec's YAML wherever the source actually gives an order. Where the source gives levels only approximately, label them approximate rather than inventing precision.
Normalise the mage and druid leveling talent orders into pipe tables matching the shape the other classes already use (mage wrote its orders as a run-on prose line, druid had no table at all), so the level-by-level order can be rendered on the seven spec guides that show none: mage/arcane-pvp, mage/frost-pvp, druid/restoration-healer, druid/balance-ranged-dps, druid/balance-pvp, druid/feral-pvp, druid/restoration-pvp. This is a source-shape fix in synthesis/classes/{mage,druid}/leveling.md; adding a second parser to the site generator was deliberately rejected as the alternative. A later addition to this same ask extended the shape fix to all nine classes rather than only those two, because the nine tables disagreed in ways that break rendering downstream: shaman carried a trailing column of talent-calculator URLs pointing at an offline, undecodable site; shaman, warlock and paladin packed an entire bracket of ordering into a single cell so a step became a paragraph; and column headers varied (Order/Talent/Points/Reaches-at, Levels/Talent/Reason, Level/Tree/Talent/Points/Why, Levels/Points/Order/Link). Converge on the one shape most classes already use, drop the dead URL column, split packed cells into one row per step, change no recommendation, keep every citation attached to its row, and where splitting would require inventing a level boundary the source does not give, keep the rows the source supports and say so.
Fill stat_priority in the per-spec YAML wherever the sources support it. Before this, only the five warrior guides had it; the rest had only stat_weights, whose keys include notes, talents and camp names that are not stats at all, which is why deriving a gearing order from them produced false claims. Curate a real sourced stat priority where the source gives one, and LEAVE IT ABSENT where it does not. An absent field is the correct, honest outcome; a guessed one is not.
Hard rules the captain set for all four: invent nothing - every claim traces to a cited forum post, wiki page or staff statement, following synthesis/CONVENTIONS.md; where the sources do not settle something, record the gap explicitly rather than filling it, because the repo's own rule is that gaps are listed as gaps. Staff statements outrank player claims; mark agreement as consensus, single source or contested the way existing records do, and keep the authority tiers straight. Do not rewrite existing prose beyond the talent-table normalisation. Do not touch site/src/** - a separate branch owns the presentation. python3 scrape/validate.py must pass (it checks that every cited post exists in the archive and that all data files parse), and from site/, python3 scripts/build-data.py then npm run check and npm run smoke must all pass, with site/src/data/*.json regenerated and committed since it is generated. Work class by class and keep each class's citations correct rather than racing to breadth; a thin correct record beats a rich invented one. If the sources genuinely do not support one of these fields for a given class or spec, that is a finding, not a failure - record it and report any class where the gap is large enough that the captain should know.
What Changed
structured/classes/<class>/leveling.yamlfor all nine classes: a cited, machine-readable leveling kill loop (spec verdict, opener chain, priority, mob-selection and weapon rules, per-spec stat priority, gaps) extracted from thesynthesis/classes/<class>/leveling.mdplaystyle sections.talents.leveling_orderandstat_priorityin the per-spec playbooks wherever the sources support them — including bracket-approximate levels and honest absences where no order is given — and regeneratesite/src/data/classes.json/meta.jsonfrom them.synthesis/classes/**/leveling.mdinto one| Levels | Talent | Note |table per build, splitting packed cells, dropping the offline calculator-URL column, and recording only the levels the sources state; update AGENTS.md and the local-server prompt to document the new schemas.Risk Assessment
✅ Low: The change is data and documentation only (no executable code paths), every value is traceable to cited posts, all prior review findings are correctly resolved, and the generated site data is regenerated in sync.
Testing
Baseline configured checks passed before this run. I rebuilt site/src/data with build-data.py (classes.json came out byte-identical to the committed file, so the committed generated data is reproducible) and drove the real React app through an SSR render of every changed surface: mage leveling (4 orders: Frost ST/AoE, Fire, Arcane), druid leveling (1 approximate order), the seven previously orderless spec guides now showing a borrowed leveling path, paladin's two distinctly labelled build tables, warrior Arms' numbered curated stat_priority versus paladin Retribution's honest stat_weights fallback, and Hunter BM/Shaman Enhancement rendering their own talents.leveling_order. Rendered before/after snapshots confirm mage and the seven spec guides previously published no parseable order. The nine structured leveling.yaml (R11) have no runtime consumer yet - build-data.py does not read them and AGENTS.md says so - so they could not be exercised live; I verified them statically against the documented schema (all nine present, every field carries sources, every forum citation resolves, spec/role labels canonical). No product failures found.
python3 site/scripts/build-data.pythengit diff --numstat -- site/src/data/classes.jsonis empty; md5 unchanged at abc049e3b341d09eb3e23917b6c79b48 (only meta.json's timestamp/commit differ, whic…Evidence: Mage leveling page (after): 4 level-by-level talent paths
~/.no-mistakes/evidence/01M2FZ4WNGXR6QPMJASEPCRH3C/before-mage-leveling-snapshot.html)~/.no-mistakes/evidence/01M2FZ4WNGXR6QPMJASEPCRH3C/druid-leveling-snapshot.html)~/.no-mistakes/evidence/01M2FZ4WNGXR6QPMJASEPCRH3C/paladin-leveling-snapshot.html)~/.no-mistakes/evidence/01M2FZ4WNGXR6QPMJASEPCRH3C/paladin-retribution-melee-dps-snapshot.html)~/.no-mistakes/evidence/01M2FZ4WNGXR6QPMJASEPCRH3C/warrior-arms-melee-dps-snapshot.html)Evidence: Nine structured leveling.yaml schema + citation-resolution check
checked 9 leveling.yaml warrior stat_priority=1 entries paladin stat_priority=2 entries hunter stat_priority=null rogue stat_priority=null priest stat_priority=1 entries shaman stat_priority=3 entries mage stat_priority=1 entries warlock stat_priority=1 entries druid stat_priority=2 entries OK: all nine leveling.yaml satisfy the documented schema; every forum citation resolves in the archiveEvidence: Generated-data reproducibility: classes.json byte-identical after regeneration
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed (4) ✅
structured/classes/rogue/leveling.yaml:7-patch_validity.written_forstill asserts the ordered route "was reposted unchanged from 2025-08 to 2025-12", but the archive shows a progression across three sequential posts. I read them in structured/forum/posts/f47.jsonl: p=148075 (2025-08-24) gives only levels 10-27; p=149395 (2025-08-30) is a reply to Morote's follow-up ("How would you continue investing talent points after level 27?") that supplies 28-44; p=161802 (2025-12-06) answers Aftabsafi's "What PVE Talents after 44?" and continues past 44. None is a repost. The corrected sibling field in the same branch already says the opposite: structured/classes/rogue/subtlety-melee-dps.yaml:53leveling_order_sourcereads "given across three posts rather than one: p=148075 supplies the rows to level 27, p=149395 continues the route from 28 through 44 ... Shananagans continues past 44 at p=161802", and synthesis/classes/rogue/leveling.md:20 carries the same corrected wording. Becausepatch_validity.written_foris rendered (site-redesign.md maps it to the 1.18.1 chip/callout), the structured record now states a rendered false provenance that contradicts both the archive and the two surfaces corrected in this branch. This was raised in the immediately preceding review round and selected for fixing, but no fix is present in the tree. Mechanical wording correction with no product decision: rewordwritten_forto describe the three-post continuation (keeping the 2025-08 .. 2025-12 span), matching the sibling field.🔧 Fix applied.
1 warning still open:
structured/classes/rogue/leveling.yaml:59- The prior fix setspec_verdict.favored.specto the matrix's hybrid name "Ambush/Backstab hybrid (Subtlety-Combat)" but left all four favoured opener steps atspec: Subtlety(lines 59, 67, 73, 80).Subtletynow appears nowhere inspec_verdict— favoured is the hybrid, alternatives are Assassination and Combat, and the avoid entry is "deep or full Subtlety" — so a consumer that groups the opener chain by the favoured spec (the natural machine read, and what AGENTS.md §2 frames the leveling record for) gets no opener for the favoured build; before the fix the label at least matched. This is the residual of the user-directed narrow fix, which explicitly said to change onlyspec_verdict.favored.specand the avoid entry, so the question is the author's: either key the opener steps to the hybrid label too, or explicitly make the tree name (Subtlety, the tree the hybrid is built from) the documented grouping key — note the same deviation now also has favoured.spec violating AGENTS.md's "speccarries the bare canonical spec name" contract for the leveling schema.🔧 Fix applied.
1 warning still open:
structured/classes/paladin/retribution-melee-dps.yaml:82- The authoritative intent (item 4) says: "Curate a real sourced stat priority where the source gives one, and LEAVE IT ABSENT where it does not. An absent field is the correct, honest outcome; a guessed one is not." This branch newly addsstat_priority: [strength, crit, hit, haste]for Retribution, and its own adjacentstat_priority_note(line 83) concedes the array is "this repository's reading of the attack-power build, not an order any source states" and that "the relative ranking of the hit and haste tail is unsourced." So the change adds exactly the guessed order the criterion forbids. It also contradicts the same file's ownstat_weights(line 75: "hit: cap first"), which ranks hit above strength/crit, while the array puts strength and crit first. The conflict is user-visible: StatPriority.tsx rendersstat_priorityas the numbered gearing list, and no component readsstat_priority_note(grep over site/src finds it only in the generated classes.json), so the disclosure never reaches the reader and the unsourced order ships as authoritative. The intent's own remedy is to remove the field (and drop the matching "this guide's own reading" clause now added to synthesis/classes/paladin/retribution-melee-dps.md) rather than relabel it; because that removes a deliberate data choice it needs the author. If the earlier relabelling was itself the authorized resolution of this concern, treat this as that decision restated.🔧 Fix applied.
1 warning still open:
AGENTS.md:46- The previous fix round (HEAD 7e2b0bf) removed the unsourced Paladin Retributionstat_priority, taking the number of spec playbooks that carry the field from 37 to 36 (ls structured/classes/*/*.yamlminusleveling.yaml/gear.yaml= 47 files; 36 containstat_priority; the 11 without are feral-pvp, feral-tank, restoration-healer, arcane-pvp, frost-pvp, holy-pvp, retribution-melee-dps, shadow-pvp, assassination-pvp, subtlety-melee-dps, subtlety-pvp). But three docs still assert the old count: AGENTS.md:46 says "Present on 37 of 47 playbooks", site/PLAN.md:61 says "present on 37 of 47 playbooks", and scrape/prompts/site-redesign.md:46 says "stat_priority[](37 specs)". A consumer following AGENTS.md/PLAN.md now expects one more playbook to carry the field than actually does. Mechanical doc correction (37 -> 36 in all three places, or reword to avoid a brittle count); no behavior change.🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
python3 site/scripts/build-data.pythengit diff --numstat -- site/src/data/classes.jsonis empty; md5 unchanged at abc049e3b341d09eb3e23917b6c79b48 (only meta.json's timestamp/commit differ, whic…python3 scrape/validate.py && python3 site/scripts/test_data.py && cd site && npm ci && npm run check && npm run smokepython3 site/scripts/build-data.pythengit diff --numstat site/src/data/classes.json(no diff; md5 abc049e3b341d09eb3e23917b6c79b48 before and after)npx vite build --ssr src/evidence.tmp.tsx+node .smoke/evidence/evidence.tmp.js <evidencedir> <routes>rendering the real App (dark theme) at #/class/mage/leveling, #/class/druid/leveling, #/class/mage/arcane-pvp, #/class/mage/frost-pvp, #/class/druid/{restoration-healer,balance-ranged-dps,balance-pvp,feral-pvp,restoration-pvp}, #/class/paladin/leveling, #/class/paladin/retribution-melee-dps, #/class/warrior/arms-melee-dps, #/class/hunter/beast-mastery-ranged-dps, #/class/shaman/enhancement-melee-dps, #/class/rogue/levelingSame SSR harness rebuilt againstgit show 86c650e1b:site/src/data/classes.jsonto capture the pre-change pages (#/class/mage/leveling, #/class/mage/arcane-pvp, #/class/druid/feral-pvp) for before/after contrastpython3 /tmp/opencode/check_leveling2.pyschema + forum-citation-resolution check over all nine structured/classes/*/leveling.yamlPython check that every token of all 36 spec-YAML stat_priority arrays and all leveling.yaml stat_priority order arrays is in the AGENTS.md canonical vocabularygrep -rn '^#### ' synthesis/classes(none) and distinct subtitle check for multi-table files in classes.jsonBaseline configured command already passed:python3 scrape/validate.py && python3 site/scripts/test_data.py && cd site && npm ci && npm run check && npm run smoke🔧 **Document** - 1 issue found → auto-fixed ✅
synthesis/classes/hunter/beast-mastery-ranged-dps.md:119- This change's 1.18.1 leveling pass canonicalised the BM Frenzy range to 2–4:structured/classes/hunter/beast-mastery-ranged-dps.yamltalents.pointsnow recordsFrenzyat rank"2-4"(was"2-3"), andsynthesis/classes/hunter/leveling.mdline 107 now reads "Frenzy 2–4 (Lane13)". The endgame BM playbook is the markdown twin of that structured file, so it should carry the same figure. It already says 2–4 at line 36 ("Named picks: ... Frenzy 2–4") and in the quoted source set, but two spots still say 2–3: line 48's derived bot default ("... Bestial Wrath 1 + Frenzy 2–3 (rows 5–6 as reachable) ...") and line 119's agreement table ("Frenzy 2–3 points ... consensus (Bob022, SvenS2, Gorlocktheglock)"). The structured twin and the playbook now disagree, and the 2–3 summary also misstates Bob022, who argues for 4/5. The honest consolidated range is 2–4 (Bob022 2–4, SvenS2 2–3, Gorlocktheglock "5 is overkill"). I did not edit it because the change touched the structured side only and updating a recommendation range in non-normalised prose, then regenerating the committedsite/src/data/classes.jsonthat embeds this markdown, is the author's call; this repo's rule is to change no recommendation outside the sanctioned table normalisation.🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.