This handover documents the failed follow-system refactor attempt so the work can be reverted and rebuilt safely, one step at a time.
The priority is stability first. The previous attempt changed too many things at once across:
- package structure
- startup imports
- runtime construction
- UI wiring
- follow threshold editing
- leader publish behavior
- follower runtime behavior
That made it impossible to isolate the source of the client instability.
This document is written so the entire effort can be redone incrementally, with a test checkpoint after every single change.
These were the real requirements that should guide the redo:
- Move follow-related code into a dedicated
HeroAI/follow/subpackage. - Use self-describing names.
- Separate responsibilities clearly:
- leader publishes follow points
- follower consumes follow points and moves
- vector-field avoidance is its own concern
- follow editor/module UI is its own concern
- Remove legacy duplicate follow code outside the package once migration is proven safe.
- Keep the HeroAI UI access path:
Follow Formationsexisting button remains- existing quick window remains
- following module stays closed by default
- the module is only opened from HeroAI UI
- Preserve functionality:
- leader publish must still work
- follower movement must still work
- thresholds must still work
- existing client stability must not regress
The refactor changed file layout, imports, runtime object creation, UI behavior, and threshold behavior in the same pass.
That prevented controlled diagnosis.
The most suspicious failure path was importing follow code from core shared-memory bootstrap.
Specifically:
Py4GWCoreLib/GlobalCache/SharedMemory.py
This file owns very sensitive startup/runtime behavior.
At different points the refactor caused it to import follow code directly. Even after narrowing the import, this area remained high risk because it is loaded very early and any extra dependency chain here is dangerous.
The package root HeroAI.follow was made to re-export multiple follow pieces. That is convenient, but dangerous in runtime-critical paths.
If a core file imports:
HeroAI.follow
then it may also import:
- editor UI code
- vector field code
- follower runtime code
- leader publish code
even when only one symbol is needed.
There were two separate risks:
- importing code in a sensitive path
- instantiating classes in a sensitive path
Both need to be controlled independently during the redo.
The quick window was turned into mostly a launcher/deprecation shell, but the user still expected it to expose the threshold controls.
That broke expected behavior and created confusion during testing.
These are the original legacy locations that existed before the refactor:
HeroAI/following.pyHeroAI/follow_runtime.pyHeroAI/follow_movement.pyHeroAI/following_module.py
Py4GWCoreLib/GlobalCache/SharedMemory.pyWidgets/Automation/Multiboxing/HeroAI.pyHeroAI/headless_tree.pyHeroAI/ui_base.pyHeroAI/ui.pyHeroAI/windows.py
HeroAI/follow/__init__.pyHeroAI/follow/leader_publish.pyHeroAI/follow/follower_runtime.pyHeroAI/follow/vector_fields.pyHeroAI/follow/editor.pyHeroAI/follow/feature_flags.py
Do not migrate leader publish integration through Py4GWCoreLib/GlobalCache/SharedMemory.py until the new leader-publish module has already been proven safe in isolation.
This file is too sensitive.
Always import the exact file needed.
Good:
from HeroAI.follow.leader_publish import LeaderFollowPositionPublisher
from HeroAI.follow.follower_runtime import FollowerFollowExecutor
from HeroAI.follow.editor import run_follow_editor_uiBad:
from HeroAI.follow import LeaderFollowPositionPublisher
from HeroAI.follow import FollowerFollowExecutor
from HeroAI.follow import run_follow_editor_uiDo not combine:
- move
- rename
- encapsulation rewrite
- behavior change
in a single step.
Even if the end goal is to remove legacy files, do not delete them immediately.
For the redo:
- move code
- add compatibility wrappers
- test stability
- repoint one consumer at a time
- remove wrappers only after all consumers are proven safe
Avoid:
- module-level singleton creation
- eager creation in constructors tied to startup
- UI module imports in core paths
The Follow Formations Quick Settings window should be validated in a separate step after package migration is stable.
This is the safest execution plan.
Each phase should be completed and tested before moving to the next one.
Return to the last known stable baseline before any of the follow refactor work.
- Revert all current follow-refactor changes.
- Confirm the old legacy files exist again:
HeroAI/following.pyHeroAI/follow_runtime.pyHeroAI/follow_movement.pyHeroAI/following_module.py
- Confirm
SharedMemory.pyis back to its original stable import state. - Confirm the client starts normally.
- Launch client.
- Open HeroAI.
- Enter explorable area.
- Verify no startup instability/crash.
- Verify follow still behaves as it did before the refactor attempt.
Do not continue until this baseline is confirmed stable.
Create HeroAI/follow/ without changing any live import path.
- Create directory:
HeroAI/follow/
- Copy legacy files into the package without deleting the originals:
HeroAI/following.py->HeroAI/follow/leader_publish.pyHeroAI/follow_runtime.py->HeroAI/follow/follower_runtime.pyHeroAI/follow_movement.py->HeroAI/follow/vector_fields.pyHeroAI/following_module.py->HeroAI/follow/editor.py
- Add minimal
HeroAI/follow/__init__.py, but do not use it anywhere yet.
No runtime file should import from the new package in this phase.
- Compile only the new package files.
- Launch client.
- Verify behavior is unchanged.
Package exists, but nothing uses it yet.
Keep internal code mostly untouched. Only make the package filenames self-describing.
Keep these names:
leader_publish.pyfollower_runtime.pyvector_fields.pyeditor.py
Do not yet rename:
- classes
- functions
- INI keys
- runtime symbols
The only change here is file naming, not behavior or APIs.
- Compile package files.
- No live consumer changes yet.
- Launch client.
Make legacy files import from the new files, but keep all old public names unchanged.
Convert these files into compatibility wrappers:
HeroAI/following.pyHeroAI/follow_runtime.pyHeroAI/follow_movement.pyHeroAI/following_module.py
Each wrapper should re-export the exact old symbols expected by existing code.
No consumer should be changed yet. Existing callers should still import the old paths.
- Compile wrappers.
- Launch client.
- Verify startup stability.
- Verify old follow behavior still works.
If instability appears here, the problem is in package/module content or import scope, not in consumer migration.
Move the least sensitive caller to the new package path.
Widgets/Automation/Multiboxing/HeroAI.py
This is safer than touching SharedMemory.py.
- Change only one import at a time.
- Use direct submodule import, not package root import.
Example:
from HeroAI.follow.follower_runtime import ...not:
from HeroAI.follow import ...- Compile that file and the target follow module.
- Launch client.
- Verify widget path is stable.
- Verify follow still works.
If instability appears, revert only that one consumer move.
Move the next consumer.
- Repoint only
HeroAI/headless_tree.py. - Use direct submodule import only.
- Do not change behavior.
- Compile.
- Launch client.
- Validate headless path.
Only after all other follow consumers are stable should core shared-memory leader publish be migrated.
- Repoint
Py4GWCoreLib/GlobalCache/SharedMemory.pyto:
from HeroAI.follow.leader_publish import ...- Do not use:
from HeroAI.follow import ...- Do not instantiate anything new at module import level.
- If possible, keep construction lazy and only construct on first use.
- Compile
SharedMemory.py. - Cold-start client.
- Enter area where follow leader publish is active.
- Watch for instability.
This is the highest-risk migration step.
Improve code readability after migration is already stable.
- leader-publish class names
- follower-runtime class names
- vector-field config names
- editor façade names
Only rename one subsystem at a time.
Suggested order:
- leader publish names
- follower runtime names
- vector field names
- editor names
After each rename group:
- compile
- launch client
- test subsystem
Only now introduce cleaner classes and owned state.
This phase should not happen until the moved code is already stable in the new paths.
- Keep old free functions working.
- Add class façade alongside them.
- Migrate one caller.
- Test.
- Migrate next caller.
- Remove old function only after all callers are stable.
Do this:
- add
FollowerFollowExecutor - keep
execute_follower_follow(...) - migrate one caller
- test
Do not replace all runtime usage in one pass.
Redo UI cleanup only after backend/package stability is confirmed.
Follow Formationsexisting button remains.- Existing quick window remains.
- Following module remains closed by default.
- Following module is opened from HeroAI UI.
- Threshold controls in the quick window must actually work.
- Preserve existing window shell.
- Only add open/close hook to moved editor path.
- Test.
- Reintroduce threshold controls.
- Test threshold effect.
- Only then deprecate duplicate legacy controls.
Do not turn the quick window into a launcher-only shell until threshold control behavior is replaced and verified.
Verify the threshold controls really affect follower movement.
The threshold path spans:
- HeroAI quick window UI
- INI persistence
- leader publish reload
- shared HeroAI options
- follower runtime movement decision
- vector-field avoidance interaction
Use a fixed simple formation and test:
Default = 0Default = TouchDefault = AreaCombat = 0Combat = TouchCombat = AreaFlagged = 0Flagged = Area
For each:
- idle/out-of-combat test
- in-combat test
- personal-flag test
- all-flag test
You should see a clear difference in when followers decide to move.
If not:
- inspect what the leader publishes into shared options
- inspect what the follower runtime reads from
options.FollowMoveThreshold - inspect whether vector-field avoidance is masking the result
Delete old top-level follow files only after everything is stable.
HeroAI/following.pyHeroAI/follow_runtime.pyHeroAI/follow_movement.pyHeroAI/following_module.py
- all consumers use new submodule imports
- no remaining references found by search
- startup stable
- runtime stable
- UI stable
Search for legacy paths:
HeroAI.followingHeroAI.follow_runtimeHeroAI.follow_movementHeroAI.following_module
and relative equivalents.
When testing, isolate these paths conceptually:
- editor UI only
- follower runtime only
- leader publish only
- editor path only
- follower runtime path only
- leader publish path only
- combined path
If two subsystems were changed in the same test window, the result is ambiguous.
Any import in a core file that reaches:
- UI code
- editor code
- package root aggregators
Any module-level object construction such as:
publisher = ...executor = ...module = ...
in startup-sensitive code paths.
Any package __init__.py that eagerly imports large submodules and is then used in runtime-critical paths.
Changing behavior while refactoring names or file layout.
Deleting legacy wrappers before proving all consumers are safe.
If a very conservative implementation is preferred, use this exact strategy:
- Copy old files into
HeroAI/follow/. - Do not edit behavior.
- Keep old files alive as wrappers.
- Repoint exactly one consumer.
- Test.
- Repoint next consumer.
- Test.
- Repoint
SharedMemory.pylast. - Test.
- Only then rename classes.
- Only then improve encapsulation.
- Only then delete wrappers.
This is the lowest-risk path.
When starting again after revert, do exactly this:
- Confirm stable reverted baseline.
- Create
HeroAI/follow/only. - Copy
following.pyintoHeroAI/follow/leader_publish.py. - Copy
follow_runtime.pyintoHeroAI/follow/follower_runtime.py. - Copy
follow_movement.pyintoHeroAI/follow/vector_fields.py. - Copy
following_module.pyintoHeroAI/follow/editor.py. - Add minimal
__init__.py. - Do not change imports anywhere.
- Compile.
- Launch and verify unchanged behavior.
Only after that should the next session start consumer migration.
The rebuild should be treated as a staged migration, not a refactor.
The correct order is:
- move code
- preserve old behavior
- preserve old names
- preserve old import graph
- migrate one consumer
- test
- migrate next consumer
- test
- only then improve architecture
That is the only reliable way to find which exact change introduces instability.