1043 lines
32 KiB
Markdown
1043 lines
32 KiB
Markdown
# PHASE 5: INTEGRATION & TESTING - DETAILED IMPLEMENTATION SUBPLAN
|
|||
|
|
|
||
|
|
## Executive Summary
|
||
|
|
**Duration**: 40-60 hours (1-1.5 weeks)
|
||
|
|
**Team Size**: 8-10 specialized agents (all hands on deck)
|
||
|
|
**Primary Goal**: Full integration, performance validation, production readiness
|
||
|
|
**Critical Issues Verified**: All 4 issues (#1-#4) completely resolved
|
||
|
|
|
||
|
|
## Phase 5 Architecture Overview
|
||
|
|
|
||
|
|
### Core Components
|
||
|
|
```
|
||
|
|
Integration/
|
||
|
|
├── IntegrationTestSuite/
|
||
|
|
│ ├── FullSystemTests.cpp (1500 lines) - End-to-end testing
|
||
|
|
│ ├── IssueValidationTests.cpp (800 lines) - Verify all 4 issues fixed
|
||
|
|
│ ├── RegressionTests.cpp (1000 lines) - No functionality broken
|
||
|
|
│ └── StressTests.cpp (1200 lines) - Load and stability
|
||
|
|
├── Performance/
|
||
|
|
│ ├── BenchmarkSuite.cpp (800 lines) - Performance benchmarks
|
||
|
|
│ ├── ProfilerIntegration.cpp (600 lines) - Profiling tools
|
||
|
|
│ └── OptimizationReport.md (Documentation)
|
||
|
|
├── Migration/
|
||
|
|
│ ├── MigrationTool.cpp (1000 lines) - Bot migration utility
|
||
|
|
│ ├── DataConverter.cpp (800 lines) - Data format conversion
|
||
|
|
│ └── MigrationGuide.md (Documentation)
|
||
|
|
└── Documentation/
|
||
|
|
├── APIReference.md (Complete API docs)
|
||
|
|
├── DeploymentGuide.md (Production deployment)
|
||
|
|
└── TroubleshootingGuide.md (Common issues)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Detailed Task Breakdown
|
||
|
|
|
||
|
|
### Task 5.1: Integration Test Framework Setup
|
||
|
|
**Duration**: 6 hours
|
||
|
|
**Assigned Agents**:
|
||
|
|
- Primary: test-automation-engineer (framework design)
|
||
|
|
- Support: trinity-integration-tester (server integration)
|
||
|
|
- Review: code-quality-reviewer (test standards)
|
||
|
|
**Dependencies**: Phases 1-4 complete
|
||
|
|
**Deliverables**:
|
||
|
|
```cpp
|
||
|
|
// IntegrationTestFramework.h
|
||
|
|
class IntegrationTestFramework {
|
||
|
|
public:
|
||
|
|
struct TestConfiguration {
|
||
|
|
uint32_t botCount{100};
|
||
|
|
uint32_t playerCount{10};
|
||
|
|
uint32_t testDurationSeconds{300};
|
||
|
|
bool enableProfiling{true};
|
||
|
|
bool enableMemoryTracking{true};
|
||
|
|
bool enableDetailedLogging{false};
|
||
|
|
};
|
||
|
|
|
||
|
|
struct TestResult {
|
||
|
|
bool passed{false};
|
||
|
|
std::string testName;
|
||
|
|
std::chrono::milliseconds executionTime;
|
||
|
|
std::vector<std::string> errors;
|
||
|
|
std::vector<std::string> warnings;
|
||
|
|
|
||
|
|
// Performance metrics
|
||
|
|
double avgCpuUsage{0.0};
|
||
|
|
double peakCpuUsage{0.0};
|
||
|
|
size_t avgMemoryUsage{0};
|
||
|
|
size_t peakMemoryUsage{0};
|
||
|
|
|
||
|
|
// Issue-specific validations
|
||
|
|
bool issue1Fixed{false}; // Login group behavior
|
||
|
|
bool issue2Fixed{false}; // Ranged combat
|
||
|
|
bool issue3Fixed{false}; // Melee facing
|
||
|
|
bool issue4Fixed{false}; // Logout crash
|
||
|
|
};
|
||
|
|
|
||
|
|
// Test execution
|
||
|
|
static TestResult RunIntegrationTest(const std::string& testName,
|
||
|
|
const TestConfiguration& config);
|
||
|
|
static std::vector<TestResult> RunAllTests(const TestConfiguration& config);
|
||
|
|
|
||
|
|
// Test scenarios
|
||
|
|
static TestResult TestFullBotLifecycle();
|
||
|
|
static TestResult TestCombatScenarios();
|
||
|
|
static TestResult TestGroupOperations();
|
||
|
|
static TestResult TestMemoryStability();
|
||
|
|
static TestResult TestPerformanceTargets();
|
||
|
|
|
||
|
|
// Reporting
|
||
|
|
static void GenerateReport(const std::vector<TestResult>& results,
|
||
|
|
const std::filesystem::path& outputPath);
|
||
|
|
static void LogResults(const std::vector<TestResult>& results);
|
||
|
|
|
||
|
|
private:
|
||
|
|
// Test environment setup
|
||
|
|
static void SetupTestEnvironment();
|
||
|
|
static void TeardownTestEnvironment();
|
||
|
|
|
||
|
|
// Helper methods
|
||
|
|
static std::vector<Bot*> SpawnTestBots(uint32_t count);
|
||
|
|
static std::vector<Player*> CreateTestPlayers(uint32_t count);
|
||
|
|
static Group* CreateTestGroup(const std::vector<Player*>& members);
|
||
|
|
|
||
|
|
// Monitoring
|
||
|
|
static void StartPerformanceMonitoring();
|
||
|
|
static void StopPerformanceMonitoring();
|
||
|
|
static PerformanceMetrics GetPerformanceMetrics();
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 5.2: Critical Issue Validation Tests
|
||
|
|
**Duration**: 10 hours
|
||
|
|
**Assigned Agents**:
|
||
|
|
- Primary: test-automation-engineer (test implementation)
|
||
|
|
- Support: cpp-server-debugger (issue verification)
|
||
|
|
- Review: trinity-integration-tester (validation)
|
||
|
|
**Dependencies**: Task 5.1
|
||
|
|
**Deliverables**:
|
||
|
|
```cpp
|
||
|
|
// IssueValidationTests.cpp
|
||
|
|
class IssueValidationTests {
|
||
|
|
public:
|
||
|
|
// Issue #1: Bot creates group on player login
|
||
|
|
static bool ValidateIssue1Fixed() {
|
||
|
|
auto testPlayer = CreatePlayer("TestPlayer");
|
||
|
|
auto testBot = CreateBot("TestBot");
|
||
|
|
|
||
|
|
// Configure bot to follow player
|
||
|
|
testBot->SetFollowTarget(testPlayer->GetGUID());
|
||
|
|
|
||
|
|
// Simulate player login
|
||
|
|
SimulatePlayerLogin(testPlayer);
|
||
|
|
|
||
|
|
// Wait for event processing
|
||
|
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||
|
|
|
||
|
|
// Verify bot did NOT create a group
|
||
|
|
bool groupCreated = (testBot->GetGroup() != nullptr);
|
||
|
|
bool followingCorrectly = testBot->IsFollowing(testPlayer->GetGUID());
|
||
|
|
|
||
|
|
// Clean up
|
||
|
|
CleanupTestEntities({testPlayer, testBot});
|
||
|
|
|
||
|
|
return !groupCreated && followingCorrectly;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Issue #2: Ranged bots don't maintain distance
|
||
|
|
static bool ValidateIssue2Fixed() {
|
||
|
|
auto hunterBot = CreateBot("HunterBot", CLASS_HUNTER);
|
||
|
|
auto targetDummy = CreateCreature("TargetDummy");
|
||
|
|
|
||
|
|
// Start combat
|
||
|
|
hunterBot->EngageTarget(targetDummy);
|
||
|
|
|
||
|
|
// Monitor distance over time
|
||
|
|
std::vector<float> distances;
|
||
|
|
for (int i = 0; i < 10; ++i) {
|
||
|
|
UpdateWorld(100); // 100ms update
|
||
|
|
distances.push_back(hunterBot->GetDistance(targetDummy));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Verify proper ranged distance (20-30 yards)
|
||
|
|
bool properDistance = std::all_of(distances.begin(), distances.end(),
|
||
|
|
[](float d) { return d >= 20.0f && d <= 30.0f; });
|
||
|
|
|
||
|
|
// Verify follow has 0.0 relevance during combat
|
||
|
|
float followRelevance = hunterBot->GetBehaviorRelevance(BEHAVIOR_FOLLOW);
|
||
|
|
bool followSuppressed = (followRelevance == 0.0f);
|
||
|
|
|
||
|
|
// Clean up
|
||
|
|
CleanupTestEntities({hunterBot, targetDummy});
|
||
|
|
|
||
|
|
return properDistance && followSuppressed;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Issue #3: Melee bots don't face target
|
||
|
|
static bool ValidateIssue3Fixed() {
|
||
|
|
auto warriorBot = CreateBot("WarriorBot", CLASS_WARRIOR);
|
||
|
|
auto targetDummy = CreateCreature("TargetDummy");
|
||
|
|
|
||
|
|
// Position bot behind target
|
||
|
|
warriorBot->SetPosition(targetDummy->GetPositionX() - 2.0f,
|
||
|
|
targetDummy->GetPositionY(),
|
||
|
|
targetDummy->GetPositionZ());
|
||
|
|
|
||
|
|
// Start combat
|
||
|
|
warriorBot->EngageTarget(targetDummy);
|
||
|
|
|
||
|
|
// Wait for facing adjustment
|
||
|
|
UpdateWorld(500); // 500ms for rotation
|
||
|
|
|
||
|
|
// Verify bot is facing target
|
||
|
|
float facingAngle = warriorBot->GetAngle(targetDummy);
|
||
|
|
bool properlyFacing = (facingAngle < 0.2f); // Within 0.2 radians
|
||
|
|
|
||
|
|
// Verify bot is in melee range
|
||
|
|
float distance = warriorBot->GetDistance(targetDummy);
|
||
|
|
bool inMeleeRange = (distance <= 5.0f);
|
||
|
|
|
||
|
|
// Clean up
|
||
|
|
CleanupTestEntities({warriorBot, targetDummy});
|
||
|
|
|
||
|
|
return properlyFacing && inMeleeRange;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Issue #4: Crash when group leader logs out
|
||
|
|
static bool ValidateIssue4Fixed() {
|
||
|
|
auto leader = CreatePlayer("Leader");
|
||
|
|
auto bot1 = CreateBot("Follower1");
|
||
|
|
auto bot2 = CreateBot("Follower2");
|
||
|
|
|
||
|
|
// Create group
|
||
|
|
auto group = CreateGroup();
|
||
|
|
group->AddMember(leader->GetGUID());
|
||
|
|
group->AddMember(bot1->GetGUID());
|
||
|
|
group->AddMember(bot2->GetGUID());
|
||
|
|
group->ChangeLeader(leader->GetGUID());
|
||
|
|
|
||
|
|
// Bots follow leader
|
||
|
|
bot1->SetFollowTarget(leader->GetGUID());
|
||
|
|
bot2->SetFollowTarget(leader->GetGUID());
|
||
|
|
|
||
|
|
// Store references using safe system
|
||
|
|
SafePlayerRef leaderRef(leader->GetGUID());
|
||
|
|
bot1->SetLeaderReference(leaderRef);
|
||
|
|
bot2->SetLeaderReference(leaderRef);
|
||
|
|
|
||
|
|
// Leader logs out
|
||
|
|
SimulatePlayerLogout(leader);
|
||
|
|
delete leader; // Simulate complete removal
|
||
|
|
|
||
|
|
bool noCrash = true;
|
||
|
|
try {
|
||
|
|
// Bots should handle gracefully
|
||
|
|
for (int i = 0; i < 10; ++i) {
|
||
|
|
bot1->UpdateAI(100);
|
||
|
|
bot2->UpdateAI(100);
|
||
|
|
UpdateWorld(100);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Verify references are invalid
|
||
|
|
noCrash = !bot1->GetLeaderReference().IsValid() &&
|
||
|
|
!bot2->GetLeaderReference().IsValid();
|
||
|
|
|
||
|
|
} catch (...) {
|
||
|
|
noCrash = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Clean up
|
||
|
|
CleanupTestEntities({bot1, bot2});
|
||
|
|
|
||
|
|
return noCrash;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Run all issue validation tests
|
||
|
|
static TestReport RunAllValidations() {
|
||
|
|
TestReport report;
|
||
|
|
report.timestamp = std::chrono::system_clock::now();
|
||
|
|
|
||
|
|
report.issue1Fixed = ValidateIssue1Fixed();
|
||
|
|
report.issue2Fixed = ValidateIssue2Fixed();
|
||
|
|
report.issue3Fixed = ValidateIssue3Fixed();
|
||
|
|
report.issue4Fixed = ValidateIssue4Fixed();
|
||
|
|
|
||
|
|
report.allIssuesFixed = report.issue1Fixed &&
|
||
|
|
report.issue2Fixed &&
|
||
|
|
report.issue3Fixed &&
|
||
|
|
report.issue4Fixed;
|
||
|
|
|
||
|
|
return report;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 5.3: Performance Benchmark Suite
|
||
|
|
**Duration**: 8 hours
|
||
|
|
**Assigned Agents**:
|
||
|
|
- Primary: resource-monitor-limiter (benchmark design)
|
||
|
|
- Support: windows-memory-profiler (Windows metrics)
|
||
|
|
- Review: database-optimizer (query performance)
|
||
|
|
**Dependencies**: Task 5.2
|
||
|
|
**Deliverables**:
|
||
|
|
```cpp
|
||
|
|
// BenchmarkSuite.cpp
|
||
|
|
class BenchmarkSuite {
|
||
|
|
public:
|
||
|
|
struct BenchmarkResult {
|
||
|
|
std::string benchmarkName;
|
||
|
|
std::chrono::nanoseconds avgTime;
|
||
|
|
std::chrono::nanoseconds minTime;
|
||
|
|
std::chrono::nanoseconds maxTime;
|
||
|
|
std::chrono::nanoseconds p95Time;
|
||
|
|
std::chrono::nanoseconds p99Time;
|
||
|
|
uint64_t iterations;
|
||
|
|
double throughput; // operations per second
|
||
|
|
};
|
||
|
|
|
||
|
|
// Core benchmarks
|
||
|
|
static BenchmarkResult BenchmarkBotCreation() {
|
||
|
|
return RunBenchmark("Bot Creation", []() {
|
||
|
|
auto bot = std::make_unique<Bot>();
|
||
|
|
bot->Initialize();
|
||
|
|
// Bot destructor handles cleanup
|
||
|
|
}, 1000);
|
||
|
|
}
|
||
|
|
|
||
|
|
static BenchmarkResult BenchmarkAIUpdate() {
|
||
|
|
auto bot = CreateTestBot();
|
||
|
|
return RunBenchmark("AI Update", [&bot]() {
|
||
|
|
bot->UpdateAI(100); // 100ms diff
|
||
|
|
}, 10000);
|
||
|
|
}
|
||
|
|
|
||
|
|
static BenchmarkResult BenchmarkBehaviorSelection() {
|
||
|
|
auto manager = std::make_unique<BehaviorManager>();
|
||
|
|
BehaviorContext context;
|
||
|
|
context.SetInCombat(true);
|
||
|
|
|
||
|
|
return RunBenchmark("Behavior Selection", [&]() {
|
||
|
|
auto behavior = manager->GetHighestPriorityBehavior(context);
|
||
|
|
}, 100000);
|
||
|
|
}
|
||
|
|
|
||
|
|
static BenchmarkResult BenchmarkReferenceAccess() {
|
||
|
|
auto player = CreateTestPlayer();
|
||
|
|
SafePlayerRef ref(player->GetGUID());
|
||
|
|
|
||
|
|
return RunBenchmark("Safe Reference Access", [&ref]() {
|
||
|
|
if (auto* p = ref.GetIfValid()) {
|
||
|
|
p->GetLevel();
|
||
|
|
}
|
||
|
|
}, 1000000);
|
||
|
|
}
|
||
|
|
|
||
|
|
static BenchmarkResult BenchmarkEventDispatch() {
|
||
|
|
auto& eventSystem = BotEventSystem::Instance();
|
||
|
|
|
||
|
|
// Subscribe to test event
|
||
|
|
auto subId = eventSystem.Subscribe(
|
||
|
|
BotEventSystem::EventType::DAMAGE_DEALT,
|
||
|
|
[](const auto& e) { /* Empty handler */ });
|
||
|
|
|
||
|
|
auto result = RunBenchmark("Event Dispatch", [&]() {
|
||
|
|
auto event = std::make_unique<CombatEventData>();
|
||
|
|
event->type = BotEventSystem::EventType::DAMAGE_DEALT;
|
||
|
|
event->damage = 100;
|
||
|
|
eventSystem.PublishEvent(std::move(event));
|
||
|
|
eventSystem.ProcessEvents(1);
|
||
|
|
}, 10000);
|
||
|
|
|
||
|
|
eventSystem.Unsubscribe(subId);
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scalability benchmarks
|
||
|
|
static void BenchmarkScalability() {
|
||
|
|
std::vector<uint32_t> botCounts = {10, 50, 100, 500, 1000, 5000};
|
||
|
|
|
||
|
|
for (uint32_t count : botCounts) {
|
||
|
|
auto bots = SpawnBots(count);
|
||
|
|
|
||
|
|
auto start = std::chrono::high_resolution_clock::now();
|
||
|
|
|
||
|
|
// Run for 60 seconds
|
||
|
|
for (int i = 0; i < 600; ++i) {
|
||
|
|
for (auto* bot : bots) {
|
||
|
|
bot->UpdateAI(100);
|
||
|
|
}
|
||
|
|
UpdateWorld(100);
|
||
|
|
}
|
||
|
|
|
||
|
|
auto end = std::chrono::high_resolution_clock::now();
|
||
|
|
auto duration = end - start;
|
||
|
|
|
||
|
|
double cpuPerBot = GetCPUUsage() / count;
|
||
|
|
size_t memoryPerBot = GetMemoryUsage() / count;
|
||
|
|
|
||
|
|
LOG_BENCHMARK("Bots: %u, CPU/bot: %.4f%%, Memory/bot: %.2f MB",
|
||
|
|
count, cpuPerBot * 100, memoryPerBot / (1024.0 * 1024.0));
|
||
|
|
|
||
|
|
// Clean up
|
||
|
|
DespawnBots(bots);
|
||
|
|
|
||
|
|
// Verify targets
|
||
|
|
ASSERT(cpuPerBot < 0.001); // <0.1% CPU per bot
|
||
|
|
ASSERT(memoryPerBot < 10 * 1024 * 1024); // <10MB per bot
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
template<typename Func>
|
||
|
|
static BenchmarkResult RunBenchmark(const std::string& name,
|
||
|
|
Func&& func,
|
||
|
|
uint64_t iterations) {
|
||
|
|
std::vector<std::chrono::nanoseconds> times;
|
||
|
|
times.reserve(iterations);
|
||
|
|
|
||
|
|
// Warmup
|
||
|
|
for (uint64_t i = 0; i < std::min(uint64_t(100), iterations / 10); ++i) {
|
||
|
|
func();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Actual benchmark
|
||
|
|
for (uint64_t i = 0; i < iterations; ++i) {
|
||
|
|
auto start = std::chrono::high_resolution_clock::now();
|
||
|
|
func();
|
||
|
|
auto end = std::chrono::high_resolution_clock::now();
|
||
|
|
times.push_back(end - start);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Calculate statistics
|
||
|
|
std::sort(times.begin(), times.end());
|
||
|
|
|
||
|
|
BenchmarkResult result;
|
||
|
|
result.benchmarkName = name;
|
||
|
|
result.iterations = iterations;
|
||
|
|
result.minTime = times.front();
|
||
|
|
result.maxTime = times.back();
|
||
|
|
result.p95Time = times[iterations * 95 / 100];
|
||
|
|
result.p99Time = times[iterations * 99 / 100];
|
||
|
|
|
||
|
|
auto total = std::accumulate(times.begin(), times.end(),
|
||
|
|
std::chrono::nanoseconds(0));
|
||
|
|
result.avgTime = total / iterations;
|
||
|
|
|
||
|
|
result.throughput = 1e9 / result.avgTime.count();
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 5.4: Memory and Resource Validation
|
||
|
|
**Duration**: 8 hours
|
||
|
|
**Assigned Agents**:
|
||
|
|
- Primary: windows-memory-profiler (memory analysis)
|
||
|
|
- Support: resource-monitor-limiter (resource tracking)
|
||
|
|
- Review: cpp-server-debugger (leak detection)
|
||
|
|
**Dependencies**: Task 5.3
|
||
|
|
**Deliverables**:
|
||
|
|
```cpp
|
||
|
|
// MemoryValidation.cpp
|
||
|
|
class MemoryValidation {
|
||
|
|
public:
|
||
|
|
struct MemoryReport {
|
||
|
|
size_t initialMemory;
|
||
|
|
size_t peakMemory;
|
||
|
|
size_t finalMemory;
|
||
|
|
size_t leaked;
|
||
|
|
std::map<std::string, size_t> componentMemory;
|
||
|
|
std::vector<std::string> leakLocations;
|
||
|
|
bool passed;
|
||
|
|
};
|
||
|
|
|
||
|
|
static MemoryReport ValidateMemoryUsage() {
|
||
|
|
MemoryReport report;
|
||
|
|
|
||
|
|
// Enable memory tracking
|
||
|
|
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||
|
|
_CrtMemState initialState, finalState, diffState;
|
||
|
|
|
||
|
|
// Capture initial state
|
||
|
|
_CrtMemCheckpoint(&initialState);
|
||
|
|
report.initialMemory = GetCurrentMemoryUsage();
|
||
|
|
|
||
|
|
// Run memory stress test
|
||
|
|
RunMemoryStressTest();
|
||
|
|
|
||
|
|
// Capture final state
|
||
|
|
_CrtMemCheckpoint(&finalState);
|
||
|
|
report.finalMemory = GetCurrentMemoryUsage();
|
||
|
|
|
||
|
|
// Calculate difference
|
||
|
|
if (_CrtMemDifference(&diffState, &initialState, &finalState)) {
|
||
|
|
report.leaked = diffState.lTotalCount;
|
||
|
|
|
||
|
|
// Dump leak details
|
||
|
|
_CrtMemDumpAllObjectsSince(&initialState);
|
||
|
|
|
||
|
|
// Parse leak locations
|
||
|
|
report.leakLocations = ParseMemoryLeaks();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Component memory breakdown
|
||
|
|
report.componentMemory["BotAI"] = MeasureComponentMemory<BotAI>();
|
||
|
|
report.componentMemory["BehaviorManager"] = MeasureComponentMemory<BehaviorManager>();
|
||
|
|
report.componentMemory["SafeReferences"] = MeasureComponentMemory<SafeObjectReference<Player>>();
|
||
|
|
report.componentMemory["EventSystem"] = MeasureComponentMemory<BotEventSystem>();
|
||
|
|
|
||
|
|
// Validation
|
||
|
|
report.passed = (report.leaked < 1024) && // Less than 1KB leaked
|
||
|
|
(report.finalMemory - report.initialMemory < 100 * 1024 * 1024); // Less than 100MB growth
|
||
|
|
|
||
|
|
return report;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
static void RunMemoryStressTest() {
|
||
|
|
const uint32_t iterations = 10000;
|
||
|
|
|
||
|
|
for (uint32_t i = 0; i < iterations; ++i) {
|
||
|
|
// Bot lifecycle test
|
||
|
|
{
|
||
|
|
auto bot = std::make_unique<Bot>();
|
||
|
|
bot->Initialize();
|
||
|
|
bot->UpdateAI(100);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reference test
|
||
|
|
{
|
||
|
|
auto player = CreateTestPlayer();
|
||
|
|
SafePlayerRef ref(player->GetGUID());
|
||
|
|
auto* p = ref.GetIfValid();
|
||
|
|
delete player;
|
||
|
|
p = ref.GetIfValid(); // Should be null
|
||
|
|
}
|
||
|
|
|
||
|
|
// Event test
|
||
|
|
{
|
||
|
|
auto event = std::make_unique<BotEventSystem::EventData>();
|
||
|
|
BotEventSystem::Instance().PublishEvent(std::move(event));
|
||
|
|
BotEventSystem::Instance().ProcessEvents();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Behavior test
|
||
|
|
{
|
||
|
|
BehaviorManager manager;
|
||
|
|
BehaviorContext context;
|
||
|
|
manager.Update(nullptr, 100);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename T>
|
||
|
|
static size_t MeasureComponentMemory() {
|
||
|
|
const uint32_t samples = 1000;
|
||
|
|
size_t totalSize = 0;
|
||
|
|
|
||
|
|
std::vector<std::unique_ptr<T>> instances;
|
||
|
|
instances.reserve(samples);
|
||
|
|
|
||
|
|
size_t before = GetCurrentMemoryUsage();
|
||
|
|
|
||
|
|
for (uint32_t i = 0; i < samples; ++i) {
|
||
|
|
instances.push_back(std::make_unique<T>());
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t after = GetCurrentMemoryUsage();
|
||
|
|
|
||
|
|
return (after - before) / samples;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 5.5: Regression Testing Suite
|
||
|
|
**Duration**: 6 hours
|
||
|
|
**Assigned Agents**:
|
||
|
|
- Primary: test-automation-engineer (regression tests)
|
||
|
|
- Support: trinity-integration-tester (functionality validation)
|
||
|
|
**Dependencies**: Task 5.4
|
||
|
|
**Deliverables**:
|
||
|
|
```cpp
|
||
|
|
// RegressionTests.cpp
|
||
|
|
class RegressionTests {
|
||
|
|
public:
|
||
|
|
static bool RunAllRegressionTests() {
|
||
|
|
std::vector<std::function<bool()>> tests = {
|
||
|
|
TestCoreMovement,
|
||
|
|
TestCombatRotations,
|
||
|
|
TestQuestHandling,
|
||
|
|
TestGroupFormation,
|
||
|
|
TestLootDistribution,
|
||
|
|
TestChatResponses,
|
||
|
|
TestMountUsage,
|
||
|
|
TestVendorInteraction,
|
||
|
|
TestDungeonBehavior,
|
||
|
|
TestPvPBehavior
|
||
|
|
};
|
||
|
|
|
||
|
|
uint32_t passed = 0;
|
||
|
|
uint32_t failed = 0;
|
||
|
|
|
||
|
|
for (auto& test : tests) {
|
||
|
|
if (test()) {
|
||
|
|
passed++;
|
||
|
|
} else {
|
||
|
|
failed++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
LOG_INFO("Regression Tests: %u passed, %u failed", passed, failed);
|
||
|
|
return failed == 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
static bool TestCoreMovement() {
|
||
|
|
auto bot = CreateTestBot();
|
||
|
|
Position targetPos(100.0f, 100.0f, 10.0f);
|
||
|
|
|
||
|
|
bot->MoveTo(targetPos);
|
||
|
|
|
||
|
|
// Wait for movement
|
||
|
|
for (int i = 0; i < 50; ++i) {
|
||
|
|
bot->UpdateAI(100);
|
||
|
|
UpdateWorld(100);
|
||
|
|
if (bot->GetDistance(targetPos) < 3.0f) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return bot->GetDistance(targetPos) < 3.0f;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool TestCombatRotations() {
|
||
|
|
// Test each class rotation
|
||
|
|
std::vector<Classes> classes = {
|
||
|
|
CLASS_WARRIOR, CLASS_PALADIN, CLASS_HUNTER,
|
||
|
|
CLASS_ROGUE, CLASS_PRIEST, CLASS_DEATH_KNIGHT,
|
||
|
|
CLASS_SHAMAN, CLASS_MAGE, CLASS_WARLOCK,
|
||
|
|
CLASS_MONK, CLASS_DRUID, CLASS_DEMON_HUNTER
|
||
|
|
};
|
||
|
|
|
||
|
|
for (Classes cls : classes) {
|
||
|
|
auto bot = CreateTestBot(cls);
|
||
|
|
auto dummy = CreateTargetDummy();
|
||
|
|
|
||
|
|
bot->EngageTarget(dummy);
|
||
|
|
|
||
|
|
// Run combat for 10 seconds
|
||
|
|
for (int i = 0; i < 100; ++i) {
|
||
|
|
bot->UpdateAI(100);
|
||
|
|
UpdateWorld(100);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Verify damage was dealt
|
||
|
|
if (dummy->GetHealthPct() >= 100.0f) {
|
||
|
|
LOG_ERROR("Class %u rotation failed", cls);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Additional regression tests...
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 5.6: Migration Tool Development
|
||
|
|
**Duration**: 8 hours
|
||
|
|
**Assigned Agents**:
|
||
|
|
- Primary: database-optimizer (data migration)
|
||
|
|
- Support: cpp-server-debugger (conversion logic)
|
||
|
|
- Review: trinity-integration-tester (compatibility)
|
||
|
|
**Dependencies**: Task 5.5
|
||
|
|
**Deliverables**:
|
||
|
|
```cpp
|
||
|
|
// MigrationTool.cpp
|
||
|
|
class MigrationTool {
|
||
|
|
public:
|
||
|
|
struct MigrationConfig {
|
||
|
|
bool backupData{true};
|
||
|
|
bool validateData{true};
|
||
|
|
bool preserveCustomizations{true};
|
||
|
|
std::string backupPath{"./backup/"};
|
||
|
|
};
|
||
|
|
|
||
|
|
struct MigrationResult {
|
||
|
|
bool success{false};
|
||
|
|
uint32_t botsProcessed{0};
|
||
|
|
uint32_t botsFailed{0};
|
||
|
|
std::vector<std::string> errors;
|
||
|
|
std::chrono::milliseconds duration;
|
||
|
|
};
|
||
|
|
|
||
|
|
static MigrationResult MigrateExistingBots(const MigrationConfig& config) {
|
||
|
|
MigrationResult result;
|
||
|
|
auto startTime = std::chrono::steady_clock::now();
|
||
|
|
|
||
|
|
try {
|
||
|
|
// Step 1: Backup existing data
|
||
|
|
if (config.backupData) {
|
||
|
|
BackupExistingData(config.backupPath);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Step 2: Load existing bot data
|
||
|
|
auto oldBots = LoadOldBotData();
|
||
|
|
|
||
|
|
// Step 3: Convert each bot
|
||
|
|
for (const auto& oldBot : oldBots) {
|
||
|
|
try {
|
||
|
|
auto newBot = ConvertBot(oldBot);
|
||
|
|
|
||
|
|
// Validate conversion
|
||
|
|
if (config.validateData && !ValidateBot(newBot)) {
|
||
|
|
throw std::runtime_error("Validation failed");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Save converted bot
|
||
|
|
SaveBot(newBot);
|
||
|
|
result.botsProcessed++;
|
||
|
|
|
||
|
|
} catch (const std::exception& e) {
|
||
|
|
result.botsFailed++;
|
||
|
|
result.errors.push_back(
|
||
|
|
fmt::format("Bot {} failed: {}", oldBot.guid, e.what()));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Step 4: Update database schema
|
||
|
|
UpdateDatabaseSchema();
|
||
|
|
|
||
|
|
// Step 5: Verify migration
|
||
|
|
result.success = VerifyMigration();
|
||
|
|
|
||
|
|
} catch (const std::exception& e) {
|
||
|
|
result.success = false;
|
||
|
|
result.errors.push_back(e.what());
|
||
|
|
}
|
||
|
|
|
||
|
|
auto endTime = std::chrono::steady_clock::now();
|
||
|
|
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||
|
|
endTime - startTime);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
struct OldBotData {
|
||
|
|
ObjectGuid guid;
|
||
|
|
std::string name;
|
||
|
|
uint8_t level;
|
||
|
|
uint8_t race;
|
||
|
|
uint8_t cls;
|
||
|
|
// ... other old format fields
|
||
|
|
};
|
||
|
|
|
||
|
|
struct NewBotData {
|
||
|
|
ObjectGuid guid;
|
||
|
|
BotProfile profile;
|
||
|
|
BehaviorConfiguration behavior;
|
||
|
|
SafeReferences references;
|
||
|
|
// ... new format fields
|
||
|
|
};
|
||
|
|
|
||
|
|
static NewBotData ConvertBot(const OldBotData& oldBot) {
|
||
|
|
NewBotData newBot;
|
||
|
|
|
||
|
|
// Convert basic data
|
||
|
|
newBot.guid = oldBot.guid;
|
||
|
|
newBot.profile.name = oldBot.name;
|
||
|
|
newBot.profile.level = oldBot.level;
|
||
|
|
newBot.profile.race = Races(oldBot.race);
|
||
|
|
newBot.profile.cls = Classes(oldBot.cls);
|
||
|
|
|
||
|
|
// Initialize new systems
|
||
|
|
newBot.behavior = CreateDefaultBehaviorConfig(newBot.profile.cls);
|
||
|
|
newBot.references.Clear(); // Start with clean references
|
||
|
|
|
||
|
|
return newBot;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 5.7: Production Deployment Preparation
|
||
|
|
**Duration**: 6 hours
|
||
|
|
**Assigned Agents**:
|
||
|
|
- Primary: code-quality-reviewer (deployment checklist)
|
||
|
|
- Support: resource-monitor-limiter (production config)
|
||
|
|
- Review: trinity-integration-tester (final validation)
|
||
|
|
**Dependencies**: Task 5.6
|
||
|
|
**Deliverables**:
|
||
|
|
```markdown
|
||
|
|
# Production Deployment Guide
|
||
|
|
|
||
|
|
## Pre-Deployment Checklist
|
||
|
|
- [ ] All 4 critical issues verified fixed
|
||
|
|
- [ ] Performance targets met (<0.1% CPU, <10MB memory per bot)
|
||
|
|
- [ ] Zero memory leaks confirmed
|
||
|
|
- [ ] All tests passing (100% coverage)
|
||
|
|
- [ ] Documentation complete
|
||
|
|
- [ ] Migration tool tested
|
||
|
|
- [ ] Rollback procedure tested
|
||
|
|
|
||
|
|
## Deployment Steps
|
||
|
|
1. **Backup Current System**
|
||
|
|
```bash
|
||
|
|
./scripts/backup_production.sh
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Deploy New Code**
|
||
|
|
```bash
|
||
|
|
git checkout playerbot-dev
|
||
|
|
git pull origin playerbot-dev
|
||
|
|
cmake --build build --config Release
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Run Migration**
|
||
|
|
```bash
|
||
|
|
./bin/migration_tool --config production.json
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Verify Deployment**
|
||
|
|
```bash
|
||
|
|
./bin/integration_tests --production
|
||
|
|
```
|
||
|
|
|
||
|
|
## Configuration
|
||
|
|
```ini
|
||
|
|
# playerbots.conf (Production)
|
||
|
|
Playerbot.Enable = 1
|
||
|
|
Playerbot.MaxBots = 5000
|
||
|
|
Playerbot.Performance.CPUTarget = 0.001 # 0.1% per bot
|
||
|
|
Playerbot.Performance.MemoryTarget = 10485760 # 10MB per bot
|
||
|
|
```
|
||
|
|
|
||
|
|
## Monitoring
|
||
|
|
- CPU usage: Prometheus metrics at `/metrics`
|
||
|
|
- Memory usage: Grafana dashboard
|
||
|
|
- Error rates: ELK stack integration
|
||
|
|
- Bot behavior: Custom dashboard
|
||
|
|
|
||
|
|
## Rollback Procedure
|
||
|
|
```bash
|
||
|
|
# If issues detected:
|
||
|
|
./scripts/rollback_production.sh
|
||
|
|
```
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 5.8: Documentation Completion
|
||
|
|
**Duration**: 8 hours
|
||
|
|
**Assigned Agents**:
|
||
|
|
- Primary: code-quality-reviewer (documentation)
|
||
|
|
- Support: test-automation-engineer (examples)
|
||
|
|
- Review: cpp-architecture-optimizer (technical accuracy)
|
||
|
|
**Dependencies**: Task 5.7
|
||
|
|
**Deliverables**:
|
||
|
|
- Complete API reference (all public interfaces)
|
||
|
|
- Architecture documentation (system design)
|
||
|
|
- Troubleshooting guide (common issues)
|
||
|
|
- Performance tuning guide
|
||
|
|
- Developer onboarding guide
|
||
|
|
|
||
|
|
### Task 5.9: Final Performance Validation
|
||
|
|
**Duration**: 6 hours
|
||
|
|
**Assigned Agents**:
|
||
|
|
- Primary: resource-monitor-limiter (performance validation)
|
||
|
|
- Support: windows-memory-profiler (Windows-specific)
|
||
|
|
- Review: database-optimizer (database performance)
|
||
|
|
**Dependencies**: Task 5.8
|
||
|
|
**Deliverables**:
|
||
|
|
```cpp
|
||
|
|
// FinalValidation.cpp
|
||
|
|
class FinalValidation {
|
||
|
|
public:
|
||
|
|
static bool RunFinalValidation() {
|
||
|
|
LOG_INFO("Starting final validation for 5000 bot target...");
|
||
|
|
|
||
|
|
// Spawn 5000 bots gradually
|
||
|
|
std::vector<Bot*> bots;
|
||
|
|
for (int batch = 0; batch < 50; ++batch) {
|
||
|
|
for (int i = 0; i < 100; ++i) {
|
||
|
|
bots.push_back(CreateBot(fmt::format("Bot_{}", batch * 100 + i)));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Let system stabilize
|
||
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Run for 1 hour
|
||
|
|
auto startTime = std::chrono::steady_clock::now();
|
||
|
|
auto endTime = startTime + std::chrono::hours(1);
|
||
|
|
|
||
|
|
PerformanceMonitor monitor;
|
||
|
|
monitor.Start();
|
||
|
|
|
||
|
|
while (std::chrono::steady_clock::now() < endTime) {
|
||
|
|
for (auto* bot : bots) {
|
||
|
|
bot->UpdateAI(100);
|
||
|
|
}
|
||
|
|
UpdateWorld(100);
|
||
|
|
|
||
|
|
// Check performance every minute
|
||
|
|
if (monitor.GetElapsedSeconds() % 60 == 0) {
|
||
|
|
auto metrics = monitor.GetMetrics();
|
||
|
|
LOG_INFO("CPU: %.2f%%, Memory: %.2f MB, Bots: %u",
|
||
|
|
metrics.cpuUsage * 100,
|
||
|
|
metrics.memoryUsage / (1024.0 * 1024.0),
|
||
|
|
bots.size());
|
||
|
|
|
||
|
|
// Verify targets
|
||
|
|
double cpuPerBot = metrics.cpuUsage / bots.size();
|
||
|
|
size_t memPerBot = metrics.memoryUsage / bots.size();
|
||
|
|
|
||
|
|
if (cpuPerBot > 0.001 || memPerBot > 10 * 1024 * 1024) {
|
||
|
|
LOG_ERROR("Performance targets not met!");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
monitor.Stop();
|
||
|
|
auto finalMetrics = monitor.GetMetrics();
|
||
|
|
|
||
|
|
LOG_INFO("Final Validation Complete:");
|
||
|
|
LOG_INFO(" Total Bots: %u", bots.size());
|
||
|
|
LOG_INFO(" Avg CPU per bot: %.4f%%", (finalMetrics.cpuUsage / bots.size()) * 100);
|
||
|
|
LOG_INFO(" Avg Memory per bot: %.2f MB",
|
||
|
|
(finalMetrics.memoryUsage / bots.size()) / (1024.0 * 1024.0));
|
||
|
|
LOG_INFO(" Zero crashes: %s", finalMetrics.crashes == 0 ? "YES" : "NO");
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 5.10: Release Preparation
|
||
|
|
**Duration**: 4 hours
|
||
|
|
**Assigned Agents**:
|
||
|
|
- Primary: code-quality-reviewer (release notes)
|
||
|
|
- Support: test-automation-engineer (release validation)
|
||
|
|
**Dependencies**: Task 5.9
|
||
|
|
**Deliverables**:
|
||
|
|
- Release notes with all changes
|
||
|
|
- Version tagging (v2.0.0)
|
||
|
|
- Changelog update
|
||
|
|
- Known issues documentation
|
||
|
|
- Future roadmap
|
||
|
|
|
||
|
|
## Testing Strategy
|
||
|
|
|
||
|
|
### Integration Testing Requirements
|
||
|
|
- All 4 critical issues verified fixed
|
||
|
|
- 5000 concurrent bots tested
|
||
|
|
- 24-hour stability test passed
|
||
|
|
- Cross-platform validation (Windows/Linux)
|
||
|
|
|
||
|
|
### Performance Testing Requirements
|
||
|
|
- CPU: <0.1% per bot verified at scale
|
||
|
|
- Memory: <10MB per bot verified at scale
|
||
|
|
- Response time: <100ms for all operations
|
||
|
|
- Database: <1ms query time
|
||
|
|
|
||
|
|
### Stress Testing Requirements
|
||
|
|
- 10,000 bot spike test
|
||
|
|
- Network disconnection recovery
|
||
|
|
- Database failure recovery
|
||
|
|
- Memory exhaustion handling
|
||
|
|
|
||
|
|
## Risk Mitigation
|
||
|
|
|
||
|
|
### Deployment Risks
|
||
|
|
1. **Production Impact**: Staged rollout with monitoring
|
||
|
|
2. **Data Loss**: Complete backup before migration
|
||
|
|
3. **Performance Regression**: Continuous monitoring
|
||
|
|
4. **Compatibility Issues**: Extensive testing on production copy
|
||
|
|
|
||
|
|
## Success Criteria
|
||
|
|
|
||
|
|
### Critical Requirements
|
||
|
|
- ✅ All 4 issues (#1-#4) permanently fixed
|
||
|
|
- ✅ 5000 concurrent bots achieved
|
||
|
|
- ✅ <0.1% CPU per bot at scale
|
||
|
|
- ✅ <10MB memory per bot at scale
|
||
|
|
- ✅ Zero memory leaks over 24 hours
|
||
|
|
- ✅ 100% backward compatibility
|
||
|
|
|
||
|
|
### Quality Requirements
|
||
|
|
- ✅ 100% test coverage achieved
|
||
|
|
- ✅ Complete documentation
|
||
|
|
- ✅ Production deployment successful
|
||
|
|
- ✅ No regression in functionality
|
||
|
|
|
||
|
|
## Agent Coordination Matrix
|
||
|
|
|
||
|
|
| Task | Primary Agent | Support Agents | Review Agent |
|
||
|
|
|------|--------------|----------------|--------------|
|
||
|
|
| 5.1 | test-automation-engineer | trinity-integration-tester | code-quality-reviewer |
|
||
|
|
| 5.2 | test-automation-engineer | cpp-server-debugger | trinity-integration-tester |
|
||
|
|
| 5.3 | resource-monitor-limiter | windows-memory-profiler | database-optimizer |
|
||
|
|
| 5.4 | windows-memory-profiler | resource-monitor-limiter | cpp-server-debugger |
|
||
|
|
| 5.5 | test-automation-engineer | trinity-integration-tester | code-quality-reviewer |
|
||
|
|
| 5.6 | database-optimizer | cpp-server-debugger | trinity-integration-tester |
|
||
|
|
| 5.7 | code-quality-reviewer | resource-monitor-limiter | trinity-integration-tester |
|
||
|
|
| 5.8 | code-quality-reviewer | test-automation-engineer | cpp-architecture-optimizer |
|
||
|
|
| 5.9 | resource-monitor-limiter | windows-memory-profiler | database-optimizer |
|
||
|
|
| 5.10 | code-quality-reviewer | test-automation-engineer | cpp-architecture-optimizer |
|
||
|
|
|
||
|
|
## Final Validation Checklist
|
||
|
|
|
||
|
|
### Technical Validation
|
||
|
|
- [ ] All unit tests passing (100%)
|
||
|
|
- [ ] All integration tests passing (100%)
|
||
|
|
- [ ] Performance benchmarks met
|
||
|
|
- [ ] Memory leak scan clean
|
||
|
|
- [ ] Static analysis clean
|
||
|
|
- [ ] Code coverage >95%
|
||
|
|
|
||
|
|
### Issue Resolution Validation
|
||
|
|
- [ ] Issue #1: Login behavior fixed
|
||
|
|
- [ ] Issue #2: Ranged combat fixed
|
||
|
|
- [ ] Issue #3: Melee facing fixed
|
||
|
|
- [ ] Issue #4: Logout crash fixed
|
||
|
|
|
||
|
|
### Production Readiness
|
||
|
|
- [ ] Migration tool tested
|
||
|
|
- [ ] Rollback tested
|
||
|
|
- [ ] Documentation complete
|
||
|
|
- [ ] Monitoring configured
|
||
|
|
- [ ] Team trained
|
||
|
|
|
||
|
|
## Deliverables Summary
|
||
|
|
|
||
|
|
### Code Deliverables
|
||
|
|
- Complete refactored PlayerBot module
|
||
|
|
- Full test suite (5000+ lines)
|
||
|
|
- Migration utilities
|
||
|
|
- Monitoring integration
|
||
|
|
|
||
|
|
### Documentation Deliverables
|
||
|
|
- API reference (complete)
|
||
|
|
- Architecture guide
|
||
|
|
- Deployment guide
|
||
|
|
- Troubleshooting guide
|
||
|
|
- Performance guide
|
||
|
|
|
||
|
|
### Operational Deliverables
|
||
|
|
- Production configuration
|
||
|
|
- Monitoring dashboards
|
||
|
|
- Rollback procedures
|
||
|
|
- Support runbooks
|
||
|
|
|
||
|
|
## Phase 5 Complete Validation
|
||
|
|
|
||
|
|
### Exit Criteria
|
||
|
|
1. All 4 issues verified permanently fixed
|
||
|
|
2. 5000 bot target achieved and validated
|
||
|
|
3. Performance targets exceeded
|
||
|
|
4. Zero regressions identified
|
||
|
|
5. Production deployment successful
|
||
|
|
|
||
|
|
**Estimated Completion**: 50 hours (mid-range of 40-60 hour estimate)
|
||
|
|
**Confidence Level**: 95% (comprehensive validation approach)
|
||
|
|
**Risk Level**: Low (extensive testing and rollback procedures)
|