This file is the authoritative source for DragonSort architecture, commands, and development patterns. For cross-cutting workspace rules, see the root
../AGENTS.md.
DragonSort is a highly configurable bag sorter for World of Warcraft. It supports multiple sorting algorithms, can sort bags, bank, reagent bag, and warbank, and consolidates partial stacks before sorting.
- GitHub: https://github.com/Xerrion/DragonSort
- No automated test suite: Manual verification required for all changes.
| Version | Interface | TOC Directive |
|---|---|---|
| Retail | 110207, 120001, 120000 | ## Interface: 120001, 120000, 110207 |
| TBC Anniversary | 20505 | ## Interface-BCC: 20505 |
| MoP Classic | 50503 | ## Interface-Mists: 50503 |
Version-specific files load via BigWigsMods packager comment directives in the TOC.
Strict inward dependency: SlashCommands/Listeners -> Sorter -> Stacker/Algorithms/BagUtils -> WoW API.
| Layer | Directory | Responsibility |
|---|---|---|
| Core | DragonSort/Core/ |
Addon lifecycle, config, slash commands, minimap, engine modules |
| Locales | DragonSort/Locales/ |
11 locale files (enUS + 10 stubs) |
| Options | DragonSort_Options/ |
Companion LoadOnDemand options UI (DragonWidgets) |
| Libs | DragonSort/Libs/ |
Embedded Ace3 + utility libraries |
| File | Purpose |
|---|---|
DragonSort/Core/Init.lua |
AceAddon bootstrap, namespace, constants, OnInitialize/OnEnable/OnDisable |
DragonSort/Core/Config.lua |
AceDB defaults, schema migration |
DragonSort/Core/BagUtils.lua |
Bag ID computation, SnapshotBags, BuildSortKey, FindStackTargets |
DragonSort/Core/Algorithms.lua |
CycleSort, SelectionSort, InsertionSort, ShellSort, Sort dispatcher |
DragonSort/Core/Stacker.lua |
Async partial stack consolidation via coroutine + OnUpdate |
DragonSort/Core/Sorter.lua |
Orchestration: bank state, combat deferral, item preloading, move execution |
DragonSort/Core/MinimapIcon.lua |
LibDataBroker + LibDBIcon minimap button |
DragonSort/Core/ConfigWindow.lua |
LoadOnDemand loader for DragonSort_Options |
DragonSort/Core/SlashCommands.lua |
/dragonsort and /ds command router |
DragonSort/Locales/enUS.lua |
Base English locale (86 keys, value = true) |
DragonSort/Locales/*.lua |
10 non-English stub files (all keys commented out) |
DragonSort_Options/Core/Core.lua |
DragonWidgets bridge, tab registry, global DragonSort_Options API |
DragonSort_Options/Core/OptionsWindow.lua |
Reserved stub for future helpers |
DragonSort_Options/Tabs/GeneralTab.lua |
Sort Options + Sort Keys sections |
local ADDON_NAME, ns = ...All modules attach to ns. The namespace is exposed globally as DragonSortNS for the companion options addon.
| Sub-table | Set by |
|---|---|
ns.Addon |
Core/Init.lua |
ns.BagUtils |
Core/BagUtils.lua |
ns.Algorithms |
Core/Algorithms.lua |
ns.Stacker |
Core/Stacker.lua |
ns.Sorter |
Core/Sorter.lua |
ns.MinimapIcon |
Core/MinimapIcon.lua |
ns.ConfigWindow |
Core/ConfigWindow.lua |
ns.Print |
Core/Init.lua (helper) |
ns.DebugPrint |
Core/Init.lua (helper) |
ns.L |
Core/Init.lua (AceLocale) |
- Phase 1 (Virtual): Pure Lua sort using
table.sort()to build a target order in memory. - Phase 2 (Physical): Async cursor-based moves using cycle decomposition to minimize swaps, executed via coroutine + OnUpdate.
- Runs BEFORE sorting when enabled.
- Consolidates partial stacks of the same item into full stacks.
- Async via coroutine + OnUpdate.
| Algorithm | Key | Characteristic |
|---|---|---|
| Cycle Sort | "cycle" | Optimal: minimum moves (n-c swaps for n items). Default. |
| Selection Sort | "selection" | O(n^2) comparisons, O(n) swaps. |
| Insertion Sort | "insertion" | O(n^2) worst case, O(n) nearly-sorted. |
| Shell Sort | "shell" | O(n log^2 n) with Ciura gap sequence. |
ns.Sorter.StartSort(scope): Public entry point.C_Container.GetContainerItemInfo(bagID, slot): Canonical empty check.C_Container.PickupContainerItem(): Called twice to execute a swap.ITEM_UNLOCKED: Event used to gate the async move loop to avoid slot corruption.C_Item.GetItemInfoInstant(): Used for preloading; must be nil-guarded.
| Key | Type | Default | Description |
|---|---|---|---|
| algorithm | string | "cycle" | Sorting algorithm: cycle/selection/insertion/shell |
| scope | string | "bags" | What to sort: bags/bank/reagent/warbank/all |
| stackBeforeSort | boolean | true | Consolidate partial stacks before sorting |
| deferInCombat | boolean | true | Queue sort during combat, run after |
| respectDisableFlag | boolean | true | Skip bags marked as non-sortable |
| Key | Type | Default | Description |
|---|---|---|---|
| primary | string | "type" | First sort criterion: type/quality/ilvl/name/quantity |
| secondary | string | "quality" | Second criterion (tie-break for primary) |
| tertiary | string | "ilvl" | Third criterion (tie-break for secondary) |
| junkPosition | string | "end" | Where to place junk-quality items: end/beginning |
DragonSort_Options is a LoadOnDemand companion addon.
- Triggered by
/ds configor minimap right-click. - Uses DragonWidgets (git submodule at
DragonSort_Options/Libs/DragonWidgets/). - Global API:
DragonSort_Options.Open(),.Close(),.Toggle(). - Locales: Uses
GetLocale("DragonSort")(main addon name).
# Lint (must pass with 0 warnings)
luacheck .
# Manual verification steps:
# 1. /ds sort (verify bags sort correctly)
# 2. /ds stack (verify partial stacks merge)
# 3. /ds config (verify options window opens)
# 4. /ds help (verify help output)
# Mise tasks
mise run lint- C_Item.GetItemInfoInstant() may return nil: Always nil-guard for uncached items.
- Bank State: No WoW API queries bank state; tracked via
BANKFRAME_OPENED/CLOSEDevents intons.Sorter.bankIsOpen. - Cycle decomposition bug: The pivot must track its new position through multi-element cycles.
- Classic bank bag IDs: Use
NUM_BANKBAGSLOTS(notNUM_BAG_SLOTS_BANK_BAG) for Classic bank bags. - Move mechanics:
PickupContainerItemmust be called twice; wait forITEM_UNLOCKEDbetween swaps. - AceAddon Lifecycle: Use
:Enable()/:Disable()(public API), not internal hooks. - TOC Directives: Use packager comment directives (
#@retail@) for version-gating, never## Interface:mid-file.
- lint.yml: Runs on
pull_request_target. - release.yml: Handles
release-pleaseautomation. - packager.yml: Builds and uploads artifacts on release.
- Branch from
master:feat/<number>-short-desc,fix/<number>-short-desc, etc. - Reference
Closes #Nin PR body. - Squash merge only.
- Never merge release-please PRs (e.g.,
chore(master): release X.Y.Z).
- Category:
C-Bug,C-Feature,C-Performance,C-Usability,C-Code-Quality,C-Documentation,C-Localization - Area:
A-Core,A-Sorter,A-Stacker,A-Algorithms,A-Options,A-Config,A-Appearance,A-Localization,A-CI - Difficulty:
D-Good-First-Issue,D-Straightforward,D-Complex,D-Expert - Platform:
P-Retail,P-TBC-Anniversary,P-MoP-Classic,P-All-Versions
As the repo owner, always write in first-person singular ("I").
- Direct and solution-driven.
- Think in systems and frameworks.
- Bias toward concrete, copy-paste-ready solutions.
- Calm, rational, and focused.
- Addon-level
AGENTS.mdoverrides root rules. - Run
luacheck .before and after every change. - No automated tests: Provide detailed manual verification steps in PRs.
- Use the
wow-addonagent for API research; do not guess signatures. - Follow the root
../AGENTS.mdskill-loading matrix forcoderdelegations.