You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* fix(export): address adversarial review findings in PoE2 build export
CRITICAL
- PassiveSpec: include nodeNotes in CreateUndoState/RestoreUndoState so
Ctrl+Z and tree copy no longer silently erase per-node notes
- BuildExportPoE2: replace io.popen('ls -d ...') in dirExists with
os.rename(path,path) — eliminates the Windows crash risk and the shell
injection vector from unescaped path content
HIGH
- BuildExportPoE2: remove warnedNoStringId module-level global; surface
the "no stringId in tree.lua" warning as a third return value from
Export/WriteFile so each call site gets fresh state and the UI can
display it prominently (ImportTab now shows colorCodes.WARNING status)
- BuildExportPoE2: sort `order` before emitting in buildPassives and sort
SlotMap keys before iterating in buildItems to produce deterministic
JSON output regardless of LuaJIT hash table iteration order
MEDIUM
- BuildExportPoE2: PresetNextLevels anyHas now checks `levelMin or
levelMax` (was levelMax-only), preventing double-seeding of an entry
that already has only levelMin set
- BuildExportPoE2: autoBracket uses m_max(lo, ...) for hi so hi >= lo
is guaranteed even for n > 99 sets
- SkillsTab: add local m_floor alias; fix clampLvl fallback to use it
instead of the global math.floor; fix level-bracket callbacks to set
self.modFlag instead of self.build.modFlag
LOW
- PassiveTreeView: gate the per-node note popup and tooltip on
hoverNode.alloc — notes only make sense on allocated nodes; tooltip
shows existing note content when present
- BuildExportPoE2: replace factually-incorrect "typo in gameId" comment
on gemIdFor with an accurate description
Tests: add cases for levelMin-only anyHas, Export warning return value
https://claude.ai/code/session_01EvGnnyxkoMawLmWWqCTxxX
* fix(export): address ai-review follow-up findings on PR #76
- ImportTab: fix safePath gsub no-op ('^' -> '^^' to actually escape
caret characters for PoB's color-code renderer)
- BuildExportPoE2: make dirExists locale-independent by checking only
ok == true from os.rename; error strings are locale-sensitive and
cannot reliably distinguish ENOENT from EACCES
- Tests: add Export test covering the passiveWarning non-nil path
(allocated node with no stringId triggers the degraded-export warning)
https://claude.ai/code/session_01EvGnnyxkoMawLmWWqCTxxX
* test(export): cover autoBracket hi>=lo invariant for n>=100 sets
Adds an Export test that places 100 passive specs with one allocated
node in the first spec, then asserts the emitted level_interval has
lo <= hi. Regresses the underflow that existed before the m_max fix
(n=100, i=1 gave hi=floor(1/100*99)=0 < lo=1).
https://claude.ai/code/session_01EvGnnyxkoMawLmWWqCTxxX
---------
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: src/Classes/SkillsTab.lua
+4-3Lines changed: 4 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,7 @@ local pairs = pairs
7
7
localipairs=ipairs
8
8
localt_insert=table.insert
9
9
localt_remove=table.remove
10
+
localm_floor=math.floor
10
11
localm_min=math.min
11
12
localm_max=math.max
12
13
@@ -1441,7 +1442,7 @@ function SkillsTabClass:OpenSkillSetManagePopup()
1441
1442
returnfunction(buf)
1442
1443
localn=tonumber(buf)
1443
1444
ifnotnthenreturnnilend
1444
-
n=math.floor(n)
1445
+
n=m_floor(n)
1445
1446
ifn<0thenn=0end
1446
1447
ifn>100thenn=100end
1447
1448
returnn
@@ -1455,7 +1456,7 @@ function SkillsTabClass:OpenSkillSetManagePopup()
1455
1456
localset=selectedSet()
1456
1457
ifsetthen
1457
1458
set.levelMin=clampLvl(buf)
1458
-
self.build.modFlag=true
1459
+
self.modFlag=true
1459
1460
end
1460
1461
end)
1461
1462
controls.lvlMin.tooltipText="Lowest character level this skill set applies to in the exported .build (1-100). Leave blank to auto-split across skill sets."
@@ -1464,7 +1465,7 @@ function SkillsTabClass:OpenSkillSetManagePopup()
1464
1465
localset=selectedSet()
1465
1466
ifsetthen
1466
1467
set.levelMax=clampLvl(buf)
1467
-
self.build.modFlag=true
1468
+
self.modFlag=true
1468
1469
end
1469
1470
end)
1470
1471
controls.lvlMax.tooltipText="Highest character level this skill set applies to in the exported .build (1-100). Leave blank to auto-split across skill sets."
ConPrintf("[PoE2Export] tree.lua has no stringId fields; passive ids will be numeric and may not be recognised by the in-game BuildPlanner. Regenerate tree.lua via src/Export/Scripts/passivetree.lua to fix.")
284
+
localpassiveWarning=nil
285
+
ifsawMissingStringIdandnotsawStringIdthen
286
+
passiveWarning="tree.lua has no stringId fields — passive ids are numeric and may not be recognised by the in-game BuildPlanner. Regenerate tree.lua via Export/Scripts/passivetree.lua to fix."
287
+
ConPrintf("[PoE2Export] " ..passiveWarning)
289
288
end
289
+
table.sort(order)
290
290
localout= {}
291
291
for_, nodeIdinipairs(order) do
292
292
localm=merged[nodeId]
@@ -301,12 +301,11 @@ local function buildPassives(build, brackets)
301
301
t_insert(out, entry)
302
302
end
303
303
end
304
-
returnout
304
+
returnout, passiveWarning
305
305
end
306
306
307
-
-- The auto-generated Gems.lua has a typo in ~486 entries' `gameId` field
308
-
-- ("Metadata/Items/Gem/" vs the canonical "Metadata/Items/Gems/"). The table
309
-
-- KEY is correct; `gemInstance.gemId` stores that key, so prefer it.
307
+
-- Prefer the Gems.lua table key (gemId) over the gemData.gameId field;
308
+
-- the key is always authoritative whereas gameId may differ in some entries.
310
309
localfunctiongemIdFor(gem)
311
310
ifgemandgem.gemIdthenreturngem.gemIdend
312
311
returngemandgem.gemDataandgem.gemData.gameIdornil
@@ -470,7 +469,11 @@ local function buildItems(build, brackets)
470
469
localitemSet=itemsTab.itemSets[setId]
471
470
localinterval=bracketsandbrackets[setIdx] ornil
472
471
ifitemSetthen
473
-
forpobSlotName, mappinginpairs(M.SlotMap) do
472
+
localslotNames= {}
473
+
forkinpairs(M.SlotMap) dot_insert(slotNames, k) end
0 commit comments