Skip to content

feat(classes): structured leveling kill loops, sourced stat priorities, one talent-table shape - #2

Merged
stevemcqueenz merged 28 commits into
mainfrom
fm/tkb-site-synthesis
Sep 14, 2026
Merged

stevemcqueenz merged 28 commits into
mainfrom
fm/tkb-site-synthesis

Conversation

@stevemcqueenz

@stevemcqueenz stevemcqueenz commented Sep 11, 2026 •

Copy link
Copy Markdown
Owner

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.

  1. 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.

  2. 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.

  3. 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.

  4. 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

  • Add structured/classes/<class>/leveling.yaml for 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 the synthesis/classes/<class>/leveling.md playstyle sections.
  • Populate talents.leveling_order and stat_priority in the per-spec playbooks wherever the sources support them — including bracket-approximate levels and honest absences where no order is given — and regenerate site/src/data/classes.json/meta.json from them.
  • Normalize the leveling talent orders across synthesis/classes/**/leveling.md into 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.

  • Live validation: ✅ go - 7 of 8 scenarios driven live against the product
Scenario Result Live Evidence
A player opens the Mage leveling page and sees the four talent orders (Frost single-target, Frost AoE, Fire, Arcane) as level-by-level paths instead of a run-on prose paragraph ✅ pass live SSR render of the real App at #/class/mage/leveling: 4 orders and 18/18/17/19 steps with level+talent nodes (mage-leveling-snapshot.html); the same route built against the base commit data renders one…
A player opens the Druid leveling page and sees a level-by-level talent order, marked approximate where the sources give no exact level, where none rendered before ✅ pass live SSR render at #/class/druid/leveling shows the 'Talent point order by level, 10–60' order with 6 steps and the approximate marker (druid-leveling-snapshot.html); base classes.json had 0 parsed druid o…
A player opens any of the seven previously orderless spec guides (mage arcane-pvp/frost-pvp, druid restoration-healer/balance-ranged-dps/balance-pvp/feral-pvp/restoration-pvp) and sees a Leveling path… ✅ pass live All 7 routes render 'From the <Class> leveling guide' plus the borrowed steps; the same two sampled routes on base data render 'No level-by-level talent order is published for this spec.' (evidence ss…
A player opens the Paladin leveling page and sees Protection and Retribution as two separately labelled build tables, not two identically titled tables ✅ pass live SSR render at #/class/paladin/leveling shows successive subtitles 'Protection 0/31/20 (dungeons variant)' and 'Retribution 11/8/31' (paladin-leveling-snapshot.html); grep confirmes no '#### ' build he…
A player opens a spec with a curated stat_priority (Warrior Arms) and sees the numbered sourced gearing order, while Paladin Retribution (unsourced) shows the stat_weights fallback rather than a fabri… ✅ pass live SSR renders: Warrior Arms shows 1 Hit / 2 Strength / 3 Attack power / 4 Crit / 5 Agility with the consensus footnote (warrior-arms-melee-dps-snapshot.html); Paladin Retribution shows no stat_priority…
A player opens a favored leveling spec's own guide (Hunter Beast Mastery, Shaman Enhancement) and sees talents.leveling_order rendered as the leveling path rather than borrowed from the class guide ✅ pass live SSR renders show 'The sourced order the points go in' with the per-spec steps (hunter-bm-ranged-dps.html, shaman-enhancement-melee-dps.html)
A maintainer regenerates the committed site data and gets the same generated classes.json the branch commits (the parser consumed the normalized talent tables) ✅ pass live python3 site/scripts/build-data.py then git diff --numstat -- site/src/data/classes.json is empty; md5 unchanged at abc049e3b341d09eb3e23917b6c79b48 (only meta.json's timestamp/commit differ, whic…
A consumer materializes the new structured/classes/<class>/leveling.yaml (R11) and gets the documented kill-loop schema with every field citing a resolvable source ⏸️ untested no No runtime product consumer exists in this branch: site/scripts/build-data.py does not read leveling.yaml (AGENTS.md states it is 'Not yet emitted by site/scripts/build-data.py') and the presentation…
Evidence: Mage leveling page (after): 4 level-by-level talent paths
<!doctype html>
<html lang="en" data-theme="dark">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>AFTER: Mage leveling page — talent order rendered as level-by-level paths (was a run-on prose paragraph)</title>
<style>*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html{scroll-behavior:smooth;scroll-padding-top:5.5rem}body{--tw-bg-opacity: 1;background-color:rgb(var(--c-bg) / var(--tw-bg-opacity, 1));font-family:Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;--tw-text-opacity: 1;color:rgb(var(--c-text) / var(--tw-text-opacity, 1));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px}*{border-color:rgb(var(--c-line) / var(--c-line-alpha))}:focus-visible{outline:2px solid rgb(var(--c-accent));outline-offset:2px;border-radius:4px}::-moz-selection{background:rgb(var(--c-accent) / .3)}::selection{background:rgb(var(--c-accent) / .3)}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.hairline{border:1px solid rgb(var(--c-line) / var(--c-line-alpha))}.card{border:1px solid rgb(var(--c-line) / var(--c-line-alpha));border-radius:12px;--tw-bg-opacity: 1;background-color:rgb(var(--c-surface) / var(--tw-bg-opacity, 1))}.chip{display:inline-flex;align-items:center;gap:.25rem;border-radius:9999px;padding:.125rem .5rem;font-size:.75rem;line-height:1rem;font-weight:500}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.left-0{left:0}.right-0{right:0}.top-0{top:0}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.-mx-1\.5{margin-left:-.375rem;margin-right:-.375rem}.mx-1\.5{margin-left:.375rem;margin-right:.375rem}.mx-auto{margin-left:auto;margin-right:auto}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.ml-1\.5{margin-left:.375rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-auto{margin-top:auto}.line-clamp-4{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4}.line-clamp-5{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:5}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{he

... [67653 bytes truncated] ...

Maul East lashers</strong> from 50: &quot;aoe farm in dire maul east and do 50 to 60 in a few hours&quot; (amanagor 2025-01-06); wiki gold farm &quot;Lashers in Dire Maul east&quot; with a video; &quot;pull 8 lasher backs in DME and farm gold&quot; (Forumdweller). &quot;DM East whippers with herbalism&quot; made better money than SM pulls (<a href="https://forum.turtlecraft.gg/viewtopic.php?p=51016#p51016">Escapism, 2023-06-22</a>).</li>
<li><strong>Instance mega-pulls do not work</strong>: ZF graveyard safe spots removed (&quot;mobs can reach u when u jump on the wall&quot; <a href="https://forum.turtlecraft.gg/viewtopic.php?p=45321#p45321">Rogzn, 2023-05-10</a>; &quot;they changed the pathing specifically so it doesn&#39;t work&quot; <a href="https://forum.turtlecraft.gg/viewtopic.php?p=50914#p50914">Mac, 2023-06-22</a>); Maraudon/ZG pathing kites unreliable (<a href="https://forum.turtlecraft.gg/viewtopic.php?p=47099#p47099">Kairion, 2023-05-23</a>); &quot;You can still do the normal CoC zg crocs farm and the mara turtle farm tho&quot; <a href="https://forum.turtlecraft.gg/viewtopic.php?p=51019#p51019">Thegr8equalizer, 2023-06-22</a> — but &quot;You made ZG crocs unfarmable&quot; (itemization-thread player, 2024-10-30) — <strong>superseded/contested</strong>; &quot;hyperspawns&quot; in Westfall are not as in Classic (<a href="https://forum.turtlecraft.gg/viewtopic.php?p=82340#p82340">Scelus12, 2023-12-22</a>). Community disapproves of boosting (<a href="https://forum.turtlecraft.gg/viewtopic.php?p=49419#p49419">Redmagejoe</a>, <a href="https://forum.turtlecraft.gg/viewtopic.php?p=49483#p49483">Drubarrymooer</a>, 2023-06).</li>
<li>Uther&#39;s Tomb group AoE farming still happens with a partner (<a href="https://forum.turtlecraft.gg/viewtopic.php?p=167064#p167064">Ganostraza, 2026-01-31</a>).</li>
<li>Boost meta (not solo): a level-60 boomkin with Thorns kills the pulls of an un-grouped alt, &quot;you need 30 traveler tents … from 1-20 you can get 1 level every 10mins&quot; (<a href="https://forum.turtlecraft.gg/viewtopic.php?p=168464#p168464">Sineva1, 2026-02-16</a>) — single source.</li>
<li>Quest oddities: Keeper Leena (Azshara mage quest) is reached via the Temple of Arkkoran ramp at 78,38, then a stone in a round chamber, needing the Staff of Eldara (<a href="https://forum.turtlecraft.gg/viewtopic.php?p=169690#p169690">St0neh3ad, 2026-03-01</a>). Bingles&#39; quest packs (Loch Modan, level 16 with a healer): Polymorph the healer, Frost Nova → Flamestrike → Blizzard, Cone of Cold on release (Liatris 2023-04-01; pre-Blizzard-nerf).</li>
<li>Professions: Tailoring + Enchanting &quot;Standard&quot;, Alchemy + Herbalism for pots/elixirs (Chev03); Engineering matters at 60 for sapper charges (wiki); Enchanting mages can solo into Uldaman&#39;s back door for the Artisan trainer with Frost control (<a href="https://forum.turtlecraft.gg/viewtopic.php?p=6936#p6936">Roxanneflowers, 2020</a> — pre-nerf Blizzard).</li>
<li>Zone list by bracket: <strong>gap</strong> — no source gives one for mages.</li>
</ul>
</div></div></section><section id="lv-hardcore" class="scroll-mt-32 space-y-4"><div class="flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1"><h2 class="text-xl font-extrabold tracking-tight sm:text-2xl">Hardcore</h2><div class="text-sm text-muted"><span class="inline-flex items-center gap-1 text-niche" title="Hardcore"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" aria-hidden="true" focusable="false"><path d="M12 3s5 4 5 9a5 5 0 0 1-10 0c0-2 1-3.5 2-4.5 0 2 1 3 2 3 0-3 1-5.5 1-7.5Z"></path></svg> Hardcore mode</span></div></div><div class="card space-y-3 p-4 sm:p-5"><h3 class="text-base font-bold">Hardcore-mode differences</h3><div class="prose-md "><ul>
<li>Prefer Fire single-target or Frost single-target over AoE grinding: &quot;AoE farming seems a bit risky, especially if you&#39;re doing the hardcore challenge&quot; (Rudyraccoon 2024-11-10); &quot;It&#39;s a major issue for anyone playing frost mage in HC&quot; that Blizzard no longer holds packs (<a href="https://forum.turtlecraft.gg/viewtopic.php?p=158934#p158934">Soulitics, 2025-11-08</a>); Fire &quot;perfect for HC … only disadvantage is no Ice block --&gt; cold snap&quot; (Darktifa). </li>
<li>A pure-Arcane &quot;Arcanist challenge&quot; (Arcane spells only) was proposed with a Spirit/Mana Shield/wand loop (<a href="https://forum.turtlecraft.gg/viewtopic.php?p=55843#p55843">Nokid / Mativh, 2023-08</a>) — no completion reported.</li>
<li>&quot;Kamikaze&quot;/Blink-into-pack play and Icicles&#39; self-root (30% base HP shatter) are the two mage mechanics most likely to kill a HC character; Icicles is not in any leveling table (&quot;I don&#39;t recommend using icicles at all when leveling&quot;, wiki Frost FAQ).</li>
<li>Slow-and-Steady challenge levelers pick Arcane for fun (<a href="https://forum.turtlecraft.gg/viewtopic.php?p=129301#p129301">madferret, 2025-05-01</a>).</li>
</ul>
</div></div></section><section id="lv-more" class="scroll-mt-32 space-y-4"><div class="flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1"><h2 class="text-xl font-extrabold tracking-tight sm:text-2xl">More</h2></div><div class="card"><button type="button" aria-expanded="false" aria-controls=":R38s:" class="flex w-full items-center gap-2 p-4 text-left hover:bg-surface2 rounded-xl"><span class="shrink-0 text-muted transition-transform -rotate-90"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" aria-hidden="true" focusable="false"><path d="m5 9 7 7 7-7"></path></svg></span><span class="flex-1 font-semibold">Respec points</span></button></div><div class="card"><button type="button" aria-expanded="false" aria-controls=":R58s:" class="flex w-full items-center gap-2 p-4 text-left hover:bg-surface2 rounded-xl"><span class="shrink-0 text-muted transition-transform -rotate-90"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" aria-hidden="true" focusable="false"><path d="m5 9 7 7 7-7"></path></svg></span><span class="flex-1 font-semibold">Skill training priority</span></button></div></section><section id="lv-sources" class="scroll-mt-32 space-y-4"><div class="flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1"><h2 class="text-xl font-extrabold tracking-tight sm:text-2xl">Sources</h2></div><div class="card"><button type="button" aria-expanded="false" aria-controls=":R39s:" class="flex w-full items-center gap-2 p-4 text-left hover:bg-surface2 rounded-xl"><span class="shrink-0 text-muted transition-transform -rotate-90"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" aria-hidden="true" focusable="false"><path d="m5 9 7 7 7-7"></path></svg></span><span class="flex-1 font-semibold">Introduction</span></button></div><div class="card"><button type="button" aria-expanded="false" aria-controls=":R59s:" class="flex w-full items-center gap-2 p-4 text-left hover:bg-surface2 rounded-xl"><span class="shrink-0 text-muted transition-transform -rotate-90"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" aria-hidden="true" focusable="false"><path d="m5 9 7 7 7-7"></path></svg></span><span class="flex-1 font-semibold">Sources</span></button></div></section></div></div></main><footer class="mt-12 border-t"><div class="mx-auto max-w-6xl px-3 py-6 text-xs text-muted sm:px-5"><p class="mb-1">Every claim on this site comes from the archived Turtle WoW forums and is cited on the page. Nothing is invented; gaps are stated. <a href="#/about" class="text-[rgb(var(--c-accent))] hover:underline">About the data</a>.</p><p>Data generated <!-- -->2026-09-14<!-- --> · commit 8736bbe10<!-- --> · <!-- -->9<!-- --> classes ·<!-- --> <!-- -->47<!-- --> playbooks · <!-- -->323<!-- --> glossary terms.</p></div></footer>
</body></html>
  • Evidence: Mage leveling page (before): the same four orders rendered as run-on prose (local file: ~/.no-mistakes/evidence/01M2FZ4WNGXR6QPMJASEPCRH3C/before-mage-leveling-snapshot.html)
  • Evidence: Druid leveling page (after): approximate level-by-level order (local file: ~/.no-mistakes/evidence/01M2FZ4WNGXR6QPMJASEPCRH3C/druid-leveling-snapshot.html)
  • Evidence: Paladin leveling page (after): Protection and Retribution as two distinct build tables (local file: ~/.no-mistakes/evidence/01M2FZ4WNGXR6QPMJASEPCRH3C/paladin-leveling-snapshot.html)
  • Evidence: Paladin Retribution (after): no curated stat_priority, Stats falls back to stat_weights (local file: ~/.no-mistakes/evidence/01M2FZ4WNGXR6QPMJASEPCRH3C/paladin-retribution-melee-dps-snapshot.html)
  • Evidence: Warrior Arms (after): curated sourced stat_priority as numbered gearing order (local file: ~/.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 archive

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 archive
Evidence: Generated-data reproducibility: classes.json byte-identical after regeneration
$ python3 site/scripts/build-data.py
Wrote .../site/src/data: classes=9 playbooks=47 matrixRows=106 glossaryTerms=323
$ git diff --numstat -- site/src/data/classes.json # (no output)
classes.json md5 before and after: abc049e3b341d09eb3e23917b6c79b48

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_for still 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:53 leveling_order_source reads "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. Because patch_validity.written_for is 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: reword written_for to 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 set spec_verdict.favored.spec to the matrix's hybrid name "Ambush/Backstab hybrid (Subtlety-Combat)" but left all four favoured opener steps at spec: Subtlety (lines 59, 67, 73, 80). Subtlety now appears nowhere in spec_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 only spec_verdict.favored.spec and 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 "spec carries 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 adds stat_priority: [strength, crit, hit, haste] for Retribution, and its own adjacent stat_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 own stat_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 renders stat_priority as the numbered gearing list, and no component reads stat_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 Retribution stat_priority, taking the number of spec playbooks that carry the field from 37 to 36 (ls structured/classes/*/*.yaml minus leveling.yaml/gear.yaml = 47 files; 36 contain stat_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.

  • Live validation: ✅ go - 7 of 8 scenarios driven live against the product
Scenario Result Live Evidence
A player opens the Mage leveling page and sees the four talent orders (Frost single-target, Frost AoE, Fire, Arcane) as level-by-level paths instead of a run-on prose paragraph ✅ pass live SSR render of the real App at #/class/mage/leveling: 4 orders and 18/18/17/19 steps with level+talent nodes (mage-leveling-snapshot.html); the same route built against the base commit data renders one…
A player opens the Druid leveling page and sees a level-by-level talent order, marked approximate where the sources give no exact level, where none rendered before ✅ pass live SSR render at #/class/druid/leveling shows the 'Talent point order by level, 10–60' order with 6 steps and the approximate marker (druid-leveling-snapshot.html); base classes.json had 0 parsed druid o…
A player opens any of the seven previously orderless spec guides (mage arcane-pvp/frost-pvp, druid restoration-healer/balance-ranged-dps/balance-pvp/feral-pvp/restoration-pvp) and sees a Leveling path… ✅ pass live All 7 routes render 'From the <Class> leveling guide' plus the borrowed steps; the same two sampled routes on base data render 'No level-by-level talent order is published for this spec.' (evidence ss…
A player opens the Paladin leveling page and sees Protection and Retribution as two separately labelled build tables, not two identically titled tables ✅ pass live SSR render at #/class/paladin/leveling shows successive subtitles 'Protection 0/31/20 (dungeons variant)' and 'Retribution 11/8/31' (paladin-leveling-snapshot.html); grep confirmes no '#### ' build he…
A player opens a spec with a curated stat_priority (Warrior Arms) and sees the numbered sourced gearing order, while Paladin Retribution (unsourced) shows the stat_weights fallback rather than a fabri… ✅ pass live SSR renders: Warrior Arms shows 1 Hit / 2 Strength / 3 Attack power / 4 Crit / 5 Agility with the consensus footnote (warrior-arms-melee-dps-snapshot.html); Paladin Retribution shows no stat_priority…
A player opens a favored leveling spec's own guide (Hunter Beast Mastery, Shaman Enhancement) and sees talents.leveling_order rendered as the leveling path rather than borrowed from the class guide ✅ pass live SSR renders show 'The sourced order the points go in' with the per-spec steps (hunter-bm-ranged-dps.html, shaman-enhancement-melee-dps.html)
A maintainer regenerates the committed site data and gets the same generated classes.json the branch commits (the parser consumed the normalized talent tables) ✅ pass live python3 site/scripts/build-data.py then git diff --numstat -- site/src/data/classes.json is empty; md5 unchanged at abc049e3b341d09eb3e23917b6c79b48 (only meta.json's timestamp/commit differ, whic…
A consumer materializes the new structured/classes/<class>/leveling.yaml (R11) and gets the documented kill-loop schema with every field citing a resolvable source ⏸️ untested no No runtime product consumer exists in this branch: site/scripts/build-data.py does not read leveling.yaml (AGENTS.md states it is 'Not yet emitted by site/scripts/build-data.py') and the presentation…
  • python3 scrape/validate.py && python3 site/scripts/test_data.py && cd site && npm ci && npm run check && npm run smoke
  • python3 site/scripts/build-data.py then git 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 &lt;evidencedir&gt; &lt;routes&gt; 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/leveling
  • Same SSR harness rebuilt against git show 86c650e1b:site/src/data/classes.json to capture the pre-change pages (#/class/mage/leveling, #/class/mage/arcane-pvp, #/class/druid/feral-pvp) for before/after contrast
  • python3 /tmp/opencode/check_leveling2.py schema + forum-citation-resolution check over all nine structured/classes/*/leveling.yaml
  • Python 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 vocabulary
  • grep -rn &#39;^#### &#39; synthesis/classes (none) and distinct subtitle check for multi-table files in classes.json
  • Baseline configured command already passed: python3 scrape/validate.py &amp;&amp; python3 site/scripts/test_data.py &amp;&amp; cd site &amp;&amp; npm ci &amp;&amp; npm run check &amp;&amp; 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.yaml talents.points now records Frenzy at rank &#34;2-4&#34; (was &#34;2-3&#34;), and synthesis/classes/hunter/leveling.md line 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 committed site/src/data/classes.json that 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.

… 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.
…r leveling page and the glossary work

# Conflicts:
#	site/src/data/meta.json
@stevemcqueenz
stevemcqueenz merged commit e3f643b into main Sep 14, 2026
1 check passed
@stevemcqueenz
stevemcqueenz deleted the fm/tkb-site-synthesis branch September 16, 2026 22:28
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