Skip to content

[codex] Push-and-green functional closure finalization#89

Merged
Prekzursil merged 1 commit intomainfrom
codex/m1-epic7-closure-phase
Feb 28, 2026
Merged

[codex] Push-and-green functional closure finalization#89
Prekzursil merged 1 commit intomainfrom
codex/m1-epic7-closure-phase

Conversation

@Prekzursil
Copy link
Owner

@Prekzursil Prekzursil commented Feb 28, 2026

Summary

This PR finalizes the push-and-green closure wave on codex/m1-epic7-closure-phase with a single commit and updated evidence references.

Key delivered changes:

  • Hardened native host bootstrap and artifact resolution in tools/native/build-native.ps1 (absolute path normalization, deterministic re-scan, second-pass target build, explicit diagnostics).
  • Wired SWFOC_EXTENDER_HOST_PATH deterministically in tools/run-live-validation.ps1 for each live test invocation.
  • Upgraded Python fallback handling for tooling/bundle scripts (run-live-validation.ps1, collect-mod-repro-bundle.ps1) with captured execution flow.
  • Aligned promoted matrix semantics for set_unit_cap to explicit two-step fail-closed contract (enable=true, then enable=false).
  • Added helper forced-profile fallback diagnostics and deterministic ProcessLocator forced-context tests.
  • Synced runbook and board evidence links for final live matrix runs.

Why

Latest closure effort had non-deterministic host/bootstrap and context/tooling edge behavior during live evidence runs. This PR makes those behaviors deterministic and aligns live matrix expectations with fail-closed runtime safety semantics.

Risk

  • risk:high (runtime/tooling integration path touching native host and live validation harness).
  • No fail-open behavior introduced.
  • Protected merge workflow retained (no direct push to main).

Evidence (Deterministic)

  • dotnet build SwfocTrainer.sln -c Release --no-restore
  • dotnet test tests/SwfocTrainer.Tests/SwfocTrainer.Tests.csproj -c Release --no-build --filter "FullyQualifiedName!~SwfocTrainer.Tests.Profiles.Live&FullyQualifiedName!~RuntimeAttachSmokeTests" ✅ (Passed: 192, Failed: 0)
  • powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ./tools/validate-workshop-topmods.ps1 -Path tools/fixtures/workshop_topmods_sample.json -Strict
  • powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ./tools/validate-generated-profile-seed.ps1 -Path tools/fixtures/generated_profile_seeds_sample.json -Strict

Evidence (Live / Bundles)

  • Session A snapshot:
    • TestResults/runs/LIVE-EAW-20260228-204540/eaw-process-snapshot.json
  • Session B Tactical bundle:
    • TestResults/runs/LIVE-TACTICAL-20260228-211256/repro-bundle.json
  • Session C AOTR bundle:
    • TestResults/runs/LIVE-AOTR-20260228-211521/repro-bundle.json
  • Session D ROE bundle:
    • TestResults/runs/LIVE-ROE-20260228-211757/repro-bundle.json
  • Consolidated matrix:
    • TestResults/runs/LIVE-MATRIX-20260228-211757.md

Bundle strict schema validation commands all passed:

  • ... LIVE-TACTICAL-20260228-211256/repro-bundle.json
  • ... LIVE-AOTR-20260228-211521/repro-bundle.json
  • ... LIVE-ROE-20260228-211757/repro-bundle.json

Behavioral Notes

  • Promoted matrix now records set_unit_cap[1/2] and set_unit_cap[2/2] per profile when context is available.
  • Forced context remains evidence routing only and does not bypass runtime mutation safety checks.

Summary by CodeRabbit

  • Documentation

    • Updated validation runbook with clarified preflight procedures and bundle validation requirements.
  • Tests

    • Added comprehensive test coverage for helper profile selection, promoted action matrix execution, and forced context resolution.
  • Chores

    • Enhanced build and validation tooling with improved cross-platform Python resolution, native host artifact detection, and diagnostic output.

Reliability Evidence Contract

Repro bundle json: TestResults/runs/LIVE-ROE-20260228-211757/repro-bundle.json
Classification: passed

@devloai
Copy link

devloai bot commented Feb 28, 2026

Unable to trigger custom agent "Code Reviewer". You have run out of credits 😔
Please upgrade your plan or buy additional credits from the subscription page.

@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

Warning

Rate limit exceeded

@Prekzursil has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 19 minutes and 36 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 1620cc4 and 6ed7199.

📒 Files selected for processing (8)
  • TODO.md
  • docs/LIVE_VALIDATION_RUNBOOK.md
  • tests/SwfocTrainer.Tests/Profiles/LiveHeroHelperWorkflowTests.cs
  • tests/SwfocTrainer.Tests/Profiles/LivePromotedActionMatrixTests.cs
  • tests/SwfocTrainer.Tests/Runtime/ProcessLocatorForcedContextTests.cs
  • tools/collect-mod-repro-bundle.ps1
  • tools/native/build-native.ps1
  • tools/run-live-validation.ps1
📝 Walkthrough

Walkthrough

This PR introduces deterministic native host bootstrap, multi-payload promoted action matrix semantics (notably set_unit_cap enable/disable steps), environment-driven forced helper profile fallback diagnostics, cross-platform Python resolution with WSL support, and native host artifact discovery across test infrastructure, live validation tooling, and documentation.

Changes

Cohort / File(s) Summary
Test Infrastructure
tests/SwfocTrainer.Tests/Profiles/LiveHeroHelperWorkflowTests.cs, tests/SwfocTrainer.Tests/Profiles/LivePromotedActionMatrixTests.cs, tests/SwfocTrainer.Tests/Runtime/ProcessLocatorForcedContextTests.cs
Added forced helper profile mechanism with environment-driven selection logic and diagnostic instrumentation. Refactored promoted action payload handling to support multiple payloads per action (e.g., set_unit_cap with enable/disable steps) with per-step action ID construction and result aggregation. Introduced new test suite validating ProcessLocator forced context resolution via reflection, covering forced vs. detected context scenarios and environment variable parsing.
Live Validation & Repro Tooling
tools/run-live-validation.ps1, tools/collect-mod-repro-bundle.ps1
Extended Python resolution to test multiple candidates and fallback to WSL; added Invoke-CapturedCommand, Test-IsWslPythonCommand, and Convert-ToWslPath helpers for robust cross-platform invocation. Updated Test-NativeHostPreflight to return native host path and emit diagnostics; added -NativeHostPath parameter to Invoke-LiveTest. Refactored Python invocation to use captured command with WSL-aware path conversions; updated launch-context and action-status diagnostics extraction.
Native Build Process
tools/native/build-native.ps1
Added Resolve-ProviderAbsolutePath and Resolve-HostArtifact functions for absolute path resolution and prioritized artifact discovery. Integrated recursive scan fallback when initial search fails; emits diagnostic Path, SearchCount, and Source metadata. Updated Windows build flow to resolve OutDir absolutely and replace hard-coded artifact discovery with resolution-driven logic including verbose fallback build triggering.
Documentation & Planning
docs/LIVE_VALIDATION_RUNBOOK.md, TODO.md
Replaced extender host precondition with native host preflight wiring via run-live-validation.ps1; documented explicit set_unit_cap promoted matrix contract (enable then disable sequence per profile) and updated Bundle Validation evidence expectations (summary.total: 15→18, requiring per-profile set_unit_cap[1/2] and set_unit_cap[2/2] entries). Added checklist items documenting functional closure wave including deterministic native host bootstrap, promoted action matrix semantics, and strict bundle validation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

area:runtime, area:ci, area:tooling, area:docs, needs-reviewer, Review effort 4/5

Poem

🐰 A hop through bootstrap and payload arrays!
Cross-platform paths wind through WSL's ways,
Forced profiles and promoted steps aligned,
Native hosts discovered, deterministic by design—
Bundle validation tightened, diagnostics refined!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title '[codex] Push-and-green functional closure finalization' clearly summarizes the main closure wave finalization work, including native host hardening, Python fallback handling, and promoted matrix semantics alignment across multiple files and test suites.
Description check ✅ Passed The pull request description comprehensively covers all required template sections including summary, risk level (high), affected profiles, deterministic and live reliability evidence, behavioral notes, and rollback information, meeting template requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/m1-epic7-closure-phase

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Prekzursil Prekzursil added the risk:high High-risk change; strict human gate required. label Feb 28, 2026
@Prekzursil
Copy link
Owner Author

Push-and-green closure evidence update (2026-03-01)

Deterministic gate:

  • dotnet build SwfocTrainer.sln -c Release --no-restore
  • dotnet test tests/SwfocTrainer.Tests/SwfocTrainer.Tests.csproj -c Release --no-build --filter "FullyQualifiedName!~SwfocTrainer.Tests.Profiles.Live&FullyQualifiedName!~RuntimeAttachSmokeTests" ✅ (Passed: 192, Failed: 0)
  • powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ./tools/validate-workshop-topmods.ps1 -Path tools/fixtures/workshop_topmods_sample.json -Strict
  • powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ./tools/validate-generated-profile-seed.ps1 -Path tools/fixtures/generated_profile_seeds_sample.json -Strict

Live evidence bundles (strict schema validated):

  • Session A (EAW snapshot): TestResults/runs/LIVE-EAW-20260228-204540/eaw-process-snapshot.json
  • Session B (TACTICAL): TestResults/runs/LIVE-TACTICAL-20260228-211256/repro-bundle.json
  • Session C (AOTR forced): TestResults/runs/LIVE-AOTR-20260228-211521/repro-bundle.json
  • Session D (ROE forced): TestResults/runs/LIVE-ROE-20260228-211757/repro-bundle.json
  • Consolidated matrix: TestResults/runs/LIVE-MATRIX-20260228-211757.md

Live matrix summary:

  • TACTICAL: classification=passed, source=detected, failed=0, skipped=4
  • AOTR: classification=passed, source=forced, failed=0, skipped=1
  • ROE: classification=passed, source=forced, failed=0, skipped=0

Promoted matrix contract note:

  • set_unit_cap now executes in two deterministic steps where profile context is available:
    • set_unit_cap[1/2] (enable=true)
    • set_unit_cap[2/2] (enable=false)
  • Fail-closed restore behavior remains enforced; no fallback route markers are accepted for promoted matrix evidence.

@codacy-production
Copy link

codacy-production bot commented Feb 28, 2026

Codacy's Analysis Summary

0 new issue (≤ 0 issue)
0 new security issue
25 complexity
0 duplications
More details

AI Reviewer: run the reviewer on demand. As new changes are pushed, run a review below.
To trigger the first review automatically, go to your organization or repository integration settings.
AI can make mistakes. Always validate suggestions.

 
  Run Reviewer ▶︎  
 

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
native/SwfocExtender.Plugins/src/GlobalTogglePlugin.cpp (1)

173-193: ⚠️ Potential issue | 🟠 Major

Atomic state updated before write verification - may cause state inconsistency.

Lines 174-180 update the atomic state variables (freezeTimerEnabled_, fogRevealEnabled_, aiEnabled_) before the address parsing and process write occur (Lines 182-191). If the write fails, the internal state will reflect the intended value rather than the actual state.

Consider moving the atomic store after the successful write:

Proposed fix
-    const bool nextValue = request.boolValue;
-    if (request.featureId == "freeze_timer") {
-        freezeTimerEnabled_.store(nextValue);
-    } else if (request.featureId == "toggle_fog_reveal") {
-        fogRevealEnabled_.store(nextValue);
-    } else {
-        aiEnabled_.store(nextValue);
-    }
-
     std::uintptr_t targetAddress = 0;
     if (!process_mutation::TryParseAddress(resolvedAnchor->second, targetAddress)) {
         return BuildInvalidAnchorResult(request, *resolvedAnchor);
     }

     std::string writeError;
+    const bool nextValue = request.boolValue;
     const auto encoded = static_cast<std::uint8_t>(nextValue ? 1 : 0);
     if (!process_mutation::TryWriteValue<std::uint8_t>(request.processId, targetAddress, encoded, writeError)) {
         return BuildWriteFailureResult(request, *resolvedAnchor, nextValue, writeError);
     }

+    if (request.featureId == "freeze_timer") {
+        freezeTimerEnabled_.store(nextValue);
+    } else if (request.featureId == "toggle_fog_reveal") {
+        fogRevealEnabled_.store(nextValue);
+    } else {
+        aiEnabled_.store(nextValue);
+    }
+
     return BuildMutationSuccessResult(request, *resolvedAnchor, nextValue);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@native/SwfocExtender.Plugins/src/GlobalTogglePlugin.cpp` around lines 173 -
193, The atomic flags (freezeTimerEnabled_, fogRevealEnabled_, aiEnabled_) are
being stored before verifying the memory write, causing internal state to
diverge if TryParseAddress or process_mutation::TryWriteValue fails; move the
stores so they occur only after process_mutation::TryWriteValue succeeds (i.e.,
after the writeError check and before returning BuildMutationSuccessResult),
using the same nextValue computed from request.boolValue and keeping all
existing error returns (BuildInvalidAnchorResult, BuildWriteFailureResult)
unchanged.
🧹 Nitpick comments (5)
src/SwfocTrainer.Runtime/Services/NamedPipeExtenderBackend.cs (1)

151-160: Make probe-anchor merge tolerant to non-string JSON values.

GetValue<string>() can throw and drop the full merge path if one value is non-string. ToString() keeps merging resilient.

♻️ Proposed change
 private static void AppendNonEmptyAnchorValues(JsonObject sourceAnchors, JsonObject destinationAnchors)
 {
     foreach (var kv in sourceAnchors)
     {
-        var normalized = kv.Value?.GetValue<string>();
+        var normalized = kv.Value?.ToString();
         if (!string.IsNullOrWhiteSpace(normalized))
         {
             destinationAnchors[kv.Key] = normalized;
         }
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/SwfocTrainer.Runtime/Services/NamedPipeExtenderBackend.cs` around lines
151 - 160, In AppendNonEmptyAnchorValues, using kv.Value?.GetValue<string>() can
throw for non-string JSON nodes and abort the merge; change it to serialize the
value safely by using kv.Value?.ToString(), trim it, and only assign
destinationAnchors[kv.Key] when the resulting string is not null/whitespace
(e.g., var normalized = kv.Value?.ToString()?.Trim(); if
(!string.IsNullOrWhiteSpace(normalized)) destinationAnchors[kv.Key] =
normalized;). This makes the probe-anchor merge tolerant to non-string JSON
values.
native/SwfocExtender.Plugins/include/swfoc_extender/plugins/ProcessMutationHelpers.hpp (1)

72-73: Clear error on successful writes to avoid stale diagnostics.

Callers can otherwise observe an old error message even when the current write succeeded.

🧹 Proposed tweak
-    return true;
+    error.clear();
+    return true;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@native/SwfocExtender.Plugins/include/swfoc_extender/plugins/ProcessMutationHelpers.hpp`
around lines 72 - 73, In the success path that currently just does "return
true;" in ProcessMutationHelpers (the branch shown before the `#else`),
clear/reset the error diagnostic so callers don't see stale
messages—specifically clear the variable named "error" (e.g., call error.clear()
or reset the optional/error-holder used in this code) just before returning true
so successful writes remove any prior error state.
tests/SwfocTrainer.Tests/Runtime/ProcessLocatorForcedContextTests.cs (1)

71-107: Reflection-based test helpers are fragile but acceptable.

Using reflection to invoke private static methods (ResolveForcedContext, ResolveOptionsFromEnvironment) makes tests fragile to internal refactoring. However, this is a reasonable tradeoff for testing critical internal behavior without exposing it publicly.

Consider adding a comment explaining why reflection is used and which methods are being tested:

// Tests invoke private ProcessLocator methods via reflection to verify
// forced-context resolution logic without exposing internal APIs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/SwfocTrainer.Tests/Runtime/ProcessLocatorForcedContextTests.cs` around
lines 71 - 107, Add a brief explanatory comment above the reflection-based
helpers describing why private static methods are invoked via reflection (to
test internal forced-context logic without making APIs public) and list the
target methods being tested (ResolveForcedContext and
ResolveOptionsFromEnvironment); place the comment near the helper methods
InvokeResolveForcedContext and InvokeResolveOptionsFromEnvironment so future
maintainers understand the tradeoff and intent.
tools/collect-mod-repro-bundle.ps1 (1)

209-214: LASTEXITCODE check may not reliably capture the exit code.

Using Get-Variable -Name LASTEXITCODE -Scope Global -ErrorAction SilentlyContinue can miss the exit code if the native command doesn't set it, or may return a stale value from a previous command. The 2>&1 redirection captures stderr into stdout, which changes how PowerShell handles errors.

Consider using $LASTEXITCODE directly after the call:

Proposed fix
     try {
         $output = & $Command[0] `@invocationArgs` 2>&1
+        $exitCode = if ($null -ne $LASTEXITCODE) { [int]$LASTEXITCODE } else { 0 }
         return [PSCustomObject]@{
-            ExitCode = if (Get-Variable -Name LASTEXITCODE -Scope Global -ErrorAction SilentlyContinue) { [int]$global:LASTEXITCODE } else { 0 }
+            ExitCode = $exitCode
             Output = ($output | Out-String)
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/collect-mod-repro-bundle.ps1` around lines 209 - 214, The current try
block captures command output into $output and attempts to read LASTEXITCODE via
Get-Variable which can be stale; instead, immediately after invoking the
external command (& $Command[0] `@invocationArgs` 2>&1) read the automatic
$LASTEXITCODE into the ExitCode field (e.g. assign $ExitCode =
[int]($LASTEXITCODE -or 0)) to reliably capture the native process exit code;
update the PSCustomObject creation that sets ExitCode to use that direct
$LASTEXITCODE value and keep Output = ($output | Out-String) so $output and
$LASTEXITCODE are captured in the same scope for functions using $Command,
$invocationArgs and $output.
native/SwfocExtender.Plugins/src/BuildPatchPlugin.cpp (1)

230-241: Potential state inconsistency on write failure.

ApplyUnitCapState/ApplyInstantBuildState are called at lines 230-232 before the actual process mutation is attempted. If the subsequent TryWriteValue fails (lines 252-253 or 260-261), the internal atomic state (unitCapPatchEnabled_, instantBuildPatchEnabled_) will indicate "enabled" even though the process wasn't actually patched.

Consider either:

  1. Moving the state updates after successful writes, or
  2. Rolling back state on write failure
♻️ Suggested reorder: update state only on success
     if (request.featureId == "set_unit_cap") {
         if (IsUnitCapOutOfBounds(request, enablePatch)) {
             return BuildInvalidUnitCapResult(request);
         }
-
-        ApplyUnitCapState(enablePatch, request.intValue);
-    } else {
-        ApplyInstantBuildState(enablePatch);
     }

     if (!enablePatch) {
+        if (request.featureId == "set_unit_cap") {
+            ApplyUnitCapState(false, request.intValue);
+        } else {
+            ApplyInstantBuildState(false);
+        }
         return BuildDisableNoOpSuccessResult(request, enablePatch, resolvedAnchor);
     }

     // ... validation and write logic ...

     if (request.featureId == "set_unit_cap") {
         // ... write logic ...
+        ApplyUnitCapState(true, clamped);
         return BuildMutationSuccessResult(request, *resolvedAnchor, enablePatch, clamped);
     }

     // ... instant_build write logic ...
+    ApplyInstantBuildState(true);
     return BuildMutationSuccessResult(request, *resolvedAnchor, enablePatch, 1);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@native/SwfocExtender.Plugins/src/BuildPatchPlugin.cpp` around lines 230 -
241, The code updates internal flags before attempting the external patch; move
the state mutation to occur only after a successful TryWriteValue (or, if you
prefer to keep the current order, perform a rollback by reverting
unitCapPatchEnabled_ or instantBuildPatchEnabled_ when TryWriteValue fails).
Concretely: in the patch path that currently calls ApplyUnitCapState(...) or
ApplyInstantBuildState(...), defer those calls until after the corresponding
TryWriteValue(...) succeeds, or add explicit reversal calls to
ApplyUnitCapState(false, ...) / ApplyInstantBuildState(false) in the error
handling branch that handles TryWriteValue failures so unitCapPatchEnabled_ and
instantBuildPatchEnabled_ always reflect actual write success.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@native/SwfocExtender.Plugins/include/swfoc_extender/plugins/ProcessMutationHelpers.hpp`:
- Around line 31-40: The parsed 64-bit value "parsed" may exceed the width of
std::uintptr_t and get silently truncated when assigned to "address"; before
casting in the code inside ProcessMutationHelpers.hpp (the block using
std::from_chars and assigning to address), add a bounds check that the parsed
value fits in std::uintptr_t (compare parsed <=
std::numeric_limits<std::uintptr_t>::max()), return false if it does not, then
perform the static_cast to std::uintptr_t; include <limits> if not present so
the check can be performed.

In `@native/SwfocExtender.Plugins/src/BuildPatchPlugin.cpp`:
- Around line 249-257: In the set_unit_cap handling (when request.featureId ==
"set_unit_cap") the write clamps to 0..255 but validation and capability
reporting use kMinUnitCap=1 and kMaxUnitCap=100000; change the
validation/constants so the allowed range matches the 8-bit memory target (set
kMaxUnitCap to 255 and ensure kMinUnitCap is 1), update the clamp to use those
constants instead of hardcoded 0/255, and ensure the capability snapshot and any
validation logic reference kMinUnitCap/kMaxUnitCap so requests rejected/accepted
and reported bounds are consistent with process_mutation::TryWriteValue and the
value passed to BuildMutationSuccessResult.

In `@tools/collect-mod-repro-bundle.ps1`:
- Around line 74-82: The current fallback unconditionally returns the WSL
command ($wsl.Source + "-e python3") even if it was already tested and failed;
change the logic in the block after the foreach so that you only return the WSL
fallback if it actually works: call Test-PythonInterpreter with the WSL
invocation (or ensure $wsl.Source wasn't already in $candidates) and only return
@($wsl.Source, "-e", "python3") when that test succeeds; if it fails, return an
empty array (@()) instead. This modifies the post-loop behavior around
$candidates, Test-PythonInterpreter and $wsl to avoid returning a non-functional
interpreter.

In `@tools/native/build-native.ps1`:
- Around line 36-38: The throw in the Path validation if-block (the check using
[string]::IsNullOrWhiteSpace($Path)) must use a stable machine-readable
classification prefix; replace the bare throw message ("Path cannot be empty.")
with a prefixed error code (e.g. "ERR_PATH_EMPTY: Path cannot be empty.") and
apply the same pattern to the other new throw sites referenced in the review
(the throws around the other validation blocks noted). Ensure the unique prefix
is consistent, descriptive, and preserved in all three throw messages so
callers/telemetry can reliably classify the failure.
- Around line 77-80: The fallback selection of $recursiveArtifact from
$recursiveHits is nondeterministic because it takes the first hit returned by
Get-ChildItem without ordering; change the selection to sort $recursiveHits
deterministically (for example by LastWriteTime desc or FullName asc) and then
pick the first item so $recursiveArtifact is stable across runs—i.e., replace
the current Select-Object -ExpandProperty FullName -First 1 usage with a sorted
selection (Sort-Object on LastWriteTime or FullName followed by Select-Object
-First 1) to ensure reproducible artifact choice.

In `@tools/run-live-validation.ps1`:
- Around line 292-303: Update the three throw statements in the WSL path
conversion block to include stable classification codes so automation can parse
failures: change the "wsl executable not available" throw (near $wsl.Source
check) to include a code like "ERR-WSL-NOTFOUND:"; change the "Failed to convert
... using wslpath." throw (after checking $exitCode from Get-LastExitCodeOrZero)
to include a code like "ERR-WSL-WSLPATH:" and include the exitCode variable
where available; and change the "WSL path conversion produced no output" throw
(after checking $text) to include a code like "ERR-WSL-NOOUTPUT:". Ensure each
modified throw keeps the original human-readable message after the code for
context.
- Around line 328-337: The script currently throws on a non-zero $buildExitCode
before replaying $buildOutput, losing valuable build diagnostics; modify the
control flow in the block that runs & $buildScript (using $buildOutput and
Get-LastExitCodeOrZero) so that you always iterate over $buildOutput and call
Write-Information for non-empty lines (the foreach over @($buildOutput) with the
IsNullOrWhiteSpace check) before throwing the ATTACH_NATIVE_HOST_PRECHECK_FAILED
error when $buildExitCode -ne 0; keep the same diagnostic formatting and only
throw after replaying the captured output.

---

Outside diff comments:
In `@native/SwfocExtender.Plugins/src/GlobalTogglePlugin.cpp`:
- Around line 173-193: The atomic flags (freezeTimerEnabled_, fogRevealEnabled_,
aiEnabled_) are being stored before verifying the memory write, causing internal
state to diverge if TryParseAddress or process_mutation::TryWriteValue fails;
move the stores so they occur only after process_mutation::TryWriteValue
succeeds (i.e., after the writeError check and before returning
BuildMutationSuccessResult), using the same nextValue computed from
request.boolValue and keeping all existing error returns
(BuildInvalidAnchorResult, BuildWriteFailureResult) unchanged.

---

Nitpick comments:
In
`@native/SwfocExtender.Plugins/include/swfoc_extender/plugins/ProcessMutationHelpers.hpp`:
- Around line 72-73: In the success path that currently just does "return true;"
in ProcessMutationHelpers (the branch shown before the `#else`), clear/reset the
error diagnostic so callers don't see stale messages—specifically clear the
variable named "error" (e.g., call error.clear() or reset the
optional/error-holder used in this code) just before returning true so
successful writes remove any prior error state.

In `@native/SwfocExtender.Plugins/src/BuildPatchPlugin.cpp`:
- Around line 230-241: The code updates internal flags before attempting the
external patch; move the state mutation to occur only after a successful
TryWriteValue (or, if you prefer to keep the current order, perform a rollback
by reverting unitCapPatchEnabled_ or instantBuildPatchEnabled_ when
TryWriteValue fails). Concretely: in the patch path that currently calls
ApplyUnitCapState(...) or ApplyInstantBuildState(...), defer those calls until
after the corresponding TryWriteValue(...) succeeds, or add explicit reversal
calls to ApplyUnitCapState(false, ...) / ApplyInstantBuildState(false) in the
error handling branch that handles TryWriteValue failures so
unitCapPatchEnabled_ and instantBuildPatchEnabled_ always reflect actual write
success.

In `@src/SwfocTrainer.Runtime/Services/NamedPipeExtenderBackend.cs`:
- Around line 151-160: In AppendNonEmptyAnchorValues, using
kv.Value?.GetValue<string>() can throw for non-string JSON nodes and abort the
merge; change it to serialize the value safely by using kv.Value?.ToString(),
trim it, and only assign destinationAnchors[kv.Key] when the resulting string is
not null/whitespace (e.g., var normalized = kv.Value?.ToString()?.Trim(); if
(!string.IsNullOrWhiteSpace(normalized)) destinationAnchors[kv.Key] =
normalized;). This makes the probe-anchor merge tolerant to non-string JSON
values.

In `@tests/SwfocTrainer.Tests/Runtime/ProcessLocatorForcedContextTests.cs`:
- Around line 71-107: Add a brief explanatory comment above the reflection-based
helpers describing why private static methods are invoked via reflection (to
test internal forced-context logic without making APIs public) and list the
target methods being tested (ResolveForcedContext and
ResolveOptionsFromEnvironment); place the comment near the helper methods
InvokeResolveForcedContext and InvokeResolveOptionsFromEnvironment so future
maintainers understand the tradeoff and intent.

In `@tools/collect-mod-repro-bundle.ps1`:
- Around line 209-214: The current try block captures command output into
$output and attempts to read LASTEXITCODE via Get-Variable which can be stale;
instead, immediately after invoking the external command (& $Command[0]
`@invocationArgs` 2>&1) read the automatic $LASTEXITCODE into the ExitCode field
(e.g. assign $ExitCode = [int]($LASTEXITCODE -or 0)) to reliably capture the
native process exit code; update the PSCustomObject creation that sets ExitCode
to use that direct $LASTEXITCODE value and keep Output = ($output | Out-String)
so $output and $LASTEXITCODE are captured in the same scope for functions using
$Command, $invocationArgs and $output.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f9bca8 and cc5f7e7.

📒 Files selected for processing (15)
  • TODO.md
  • docs/LIVE_VALIDATION_RUNBOOK.md
  • native/SwfocExtender.Plugins/include/swfoc_extender/plugins/ProcessMutationHelpers.hpp
  • native/SwfocExtender.Plugins/src/BuildPatchPlugin.cpp
  • native/SwfocExtender.Plugins/src/EconomyPlugin.cpp
  • native/SwfocExtender.Plugins/src/GlobalTogglePlugin.cpp
  • src/SwfocTrainer.Runtime/Services/NamedPipeExtenderBackend.cs
  • src/SwfocTrainer.Runtime/Services/RuntimeAdapter.cs
  • tests/SwfocTrainer.Tests/Profiles/LiveHeroHelperWorkflowTests.cs
  • tests/SwfocTrainer.Tests/Profiles/LivePromotedActionMatrixTests.cs
  • tests/SwfocTrainer.Tests/Runtime/NamedPipeExtenderBackendTests.cs
  • tests/SwfocTrainer.Tests/Runtime/ProcessLocatorForcedContextTests.cs
  • tools/collect-mod-repro-bundle.ps1
  • tools/native/build-native.ps1
  • tools/run-live-validation.ps1

Comment on lines +31 to +40
unsigned long long parsed = 0;
const auto* begin = normalized.data();
const auto* end = begin + normalized.size();
const auto [ptr, ec] = std::from_chars(begin, end, parsed, 16);
if (ec != std::errc() || ptr != end) {
return false;
}

address = static_cast<std::uintptr_t>(parsed);
return true;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against address truncation before casting to std::uintptr_t.

A parsed 64-bit value can be silently truncated on narrower std::uintptr_t targets, which can redirect writes to the wrong address.

🛠️ Proposed fix
 `#include` <charconv>
 `#include` <cstdint>
+#include <limits>
 `#include` <string>
 `#include` <string_view>
@@
     const auto [ptr, ec] = std::from_chars(begin, end, parsed, 16);
     if (ec != std::errc() || ptr != end) {
         return false;
     }
 
+    if (parsed > static_cast<unsigned long long>(std::numeric_limits<std::uintptr_t>::max())) {
+        return false;
+    }
+
     address = static_cast<std::uintptr_t>(parsed);
     return true;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
unsigned long long parsed = 0;
const auto* begin = normalized.data();
const auto* end = begin + normalized.size();
const auto [ptr, ec] = std::from_chars(begin, end, parsed, 16);
if (ec != std::errc() || ptr != end) {
return false;
}
address = static_cast<std::uintptr_t>(parsed);
return true;
unsigned long long parsed = 0;
const auto* begin = normalized.data();
const auto* end = begin + normalized.size();
const auto [ptr, ec] = std::from_chars(begin, end, parsed, 16);
if (ec != std::errc() || ptr != end) {
return false;
}
if (parsed > static_cast<unsigned long long>(std::numeric_limits<std::uintptr_t>::max())) {
return false;
}
address = static_cast<std::uintptr_t>(parsed);
return true;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@native/SwfocExtender.Plugins/include/swfoc_extender/plugins/ProcessMutationHelpers.hpp`
around lines 31 - 40, The parsed 64-bit value "parsed" may exceed the width of
std::uintptr_t and get silently truncated when assigned to "address"; before
casting in the code inside ProcessMutationHelpers.hpp (the block using
std::from_chars and assigning to address), add a bounds check that the parsed
value fits in std::uintptr_t (compare parsed <=
std::numeric_limits<std::uintptr_t>::max()), return false if it does not, then
perform the static_cast to std::uintptr_t; include <limits> if not present so
the check can be performed.

Comment on lines +249 to +257
if (request.featureId == "set_unit_cap") {
const auto clamped = std::clamp(request.intValue, 0, 255);
const auto encoded = static_cast<std::uint8_t>(clamped);
if (!process_mutation::TryWriteValue<std::uint8_t>(request.processId, targetAddress, encoded, writeError)) {
return BuildWriteFailureResult(request, *resolvedAnchor, enablePatch, writeError);
}

return BuildMutationSuccessResult(request, *resolvedAnchor, enablePatch, clamped);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's get the file and check its size
wc -l native/SwfocExtender.Plugins/src/BuildPatchPlugin.cpp

Repository: Prekzursil/SWFOC-Mod-Menu

Length of output: 126


🏁 Script executed:

# Search for kMaxUnitCap and kMinUnitCap definitions and usages
rg -n "kMaxUnitCap|kMinUnitCap" --type cpp

Repository: Prekzursil/SWFOC-Mod-Menu

Length of output: 642


🏁 Script executed:

# Look for unit_cap related code and validation paths
rg -n "unit_cap|unitCap" --type cpp -A 2 -B 2

Repository: Prekzursil/SWFOC-Mod-Menu

Length of output: 7400


🏁 Script executed:

# Check the specific lines mentioned in the review (25-26, 115, 226, 249-257)
sed -n '20,30p' native/SwfocExtender.Plugins/src/BuildPatchPlugin.cpp

Repository: Prekzursil/SWFOC-Mod-Menu

Length of output: 563


🏁 Script executed:

# Check the validation function around line 115
sed -n '110,125p' native/SwfocExtender.Plugins/src/BuildPatchPlugin.cpp

Repository: Prekzursil/SWFOC-Mod-Menu

Length of output: 621


🏁 Script executed:

# Check the entire mutation flow from line 220-260
sed -n '220,265p' native/SwfocExtender.Plugins/src/BuildPatchPlugin.cpp

Repository: Prekzursil/SWFOC-Mod-Menu

Length of output: 1801


Bounds mismatch between validation (1–100000) and memory write clamping (0–255).

The validation at line 226 accepts intValue in range [kMinUnitCap=1, kMaxUnitCap=100000], and the capability snapshot reports these bounds to clients (lines 84–85). However, the actual memory write at line 250 clamps to [0, 255]:

const auto clamped = std::clamp(request.intValue, 0, 255);

This creates two problems:

  1. Silent clamping for large values: A request with intValue=50000 passes validation but gets silently clamped to 255 when written to memory. The success result reports clamped (255), not the original request.
  2. Lower bound inconsistency: Validation rejects intValue=0 (below kMinUnitCap), but the clamp allows it.

If the 8-bit encoding is intentional (target memory is a single byte), align the validation bounds to [1, 255] to match the actual write constraints and prevent misleading capability snapshots.

🔧 Suggested fix to align validation and write bounds
-constexpr std::int32_t kMinUnitCap = 1;
-constexpr std::int32_t kMaxUnitCap = 100000;
+constexpr std::int32_t kMinUnitCap = 1;
+constexpr std::int32_t kMaxUnitCap = 255;

And use the constants in the clamp:

-        const auto clamped = std::clamp(request.intValue, 0, 255);
+        const auto clamped = std::clamp(request.intValue, kMinUnitCap, kMaxUnitCap);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@native/SwfocExtender.Plugins/src/BuildPatchPlugin.cpp` around lines 249 -
257, In the set_unit_cap handling (when request.featureId == "set_unit_cap") the
write clamps to 0..255 but validation and capability reporting use kMinUnitCap=1
and kMaxUnitCap=100000; change the validation/constants so the allowed range
matches the 8-bit memory target (set kMaxUnitCap to 255 and ensure kMinUnitCap
is 1), update the clamp to use those constants instead of hardcoded 0/255, and
ensure the capability snapshot and any validation logic reference
kMinUnitCap/kMaxUnitCap so requests rejected/accepted and reported bounds are
consistent with process_mutation::TryWriteValue and the value passed to
BuildMutationSuccessResult.

Comment on lines +74 to 82
foreach ($candidate in $candidates) {
if (Test-PythonInterpreter -Command $candidate) {
return $candidate
}
}

if ($null -ne $wsl) {
return @($wsl.Source, "-e", "python3")
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

WSL fallback returned unconditionally after all candidate tests fail.

The loop at Lines 74-78 tests all candidates via Test-PythonInterpreter and returns the first valid one. However, if none pass, Line 80-82 returns the WSL command anyway as a fallback, even though it was already tested and failed (if WSL was in the candidates list at Line 71). This could result in returning a non-functional interpreter.

Consider returning an empty array if all candidates fail, including WSL:

Proposed fix
     foreach ($candidate in $candidates) {
         if (Test-PythonInterpreter -Command $candidate) {
             return $candidate
         }
     }

-    if ($null -ne $wsl) {
-        return @($wsl.Source, "-e", "python3")
-    }
-
     return @()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/collect-mod-repro-bundle.ps1` around lines 74 - 82, The current
fallback unconditionally returns the WSL command ($wsl.Source + "-e python3")
even if it was already tested and failed; change the logic in the block after
the foreach so that you only return the WSL fallback if it actually works: call
Test-PythonInterpreter with the WSL invocation (or ensure $wsl.Source wasn't
already in $candidates) and only return @($wsl.Source, "-e", "python3") when
that test succeeds; if it fails, return an empty array (@()) instead. This
modifies the post-loop behavior around $candidates, Test-PythonInterpreter and
$wsl to avoid returning a non-functional interpreter.

Comment on lines +36 to +38
if ([string]::IsNullOrWhiteSpace($Path)) {
throw "Path cannot be empty."
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use explicit classification codes for new failure throws.

These newly introduced failure paths should carry stable machine-readable prefixes.

🏷️ Proposed classification prefixes
-        throw "Path cannot be empty."
+        throw "NATIVE_PATH_INVALID: Path cannot be empty."
@@
-            throw "native fallback build failed for Windows mode."
+            throw "NATIVE_BUILD_FAILED: native fallback build failed for Windows mode."
@@
-        throw "native build completed but SwfocExtender.Host.exe artifact was not found under '$resolvedOutDir'."
+        throw "NATIVE_ARTIFACT_MISSING: native build completed but SwfocExtender.Host.exe artifact was not found under '$resolvedOutDir'."

As per coding guidelines "Failure conditions must use explicit classification codes".

Also applies to: 203-204, 210-212

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/native/build-native.ps1` around lines 36 - 38, The throw in the Path
validation if-block (the check using [string]::IsNullOrWhiteSpace($Path)) must
use a stable machine-readable classification prefix; replace the bare throw
message ("Path cannot be empty.") with a prefixed error code (e.g.
"ERR_PATH_EMPTY: Path cannot be empty.") and apply the same pattern to the other
new throw sites referenced in the review (the throws around the other validation
blocks noted). Ensure the unique prefix is consistent, descriptive, and
preserved in all three throw messages so callers/telemetry can reliably classify
the failure.

Comment on lines +77 to +80
$recursiveHits = @(Get-ChildItem -Path $OutDir -Filter "SwfocExtender.Host.exe" -File -Recurse -ErrorAction SilentlyContinue)
$searchCount += $recursiveHits.Count
$recursiveArtifact = $recursiveHits | Select-Object -ExpandProperty FullName -First 1
if (-not [string]::IsNullOrWhiteSpace($recursiveArtifact)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Recursive artifact fallback is currently nondeterministic.

Selecting the first recursive hit without ordering can pick stale artifacts unpredictably between runs.

🛠️ Proposed deterministic selection
-    $recursiveHits = @(Get-ChildItem -Path $OutDir -Filter "SwfocExtender.Host.exe" -File -Recurse -ErrorAction SilentlyContinue)
+    $recursiveHits = @(
+        Get-ChildItem -Path $OutDir -Filter "SwfocExtender.Host.exe" -File -Recurse -ErrorAction SilentlyContinue |
+            Sort-Object `
+                @{ Expression = { $_.LastWriteTimeUtc }; Descending = $true }, `
+                @{ Expression = { $_.FullName }; Descending = $false }
+    )
     $searchCount += $recursiveHits.Count
     $recursiveArtifact = $recursiveHits | Select-Object -ExpandProperty FullName -First 1

As per coding guidelines "Script outputs must be stable and schema-backed where practical".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/native/build-native.ps1` around lines 77 - 80, The fallback selection
of $recursiveArtifact from $recursiveHits is nondeterministic because it takes
the first hit returned by Get-ChildItem without ordering; change the selection
to sort $recursiveHits deterministically (for example by LastWriteTime desc or
FullName asc) and then pick the first item so $recursiveArtifact is stable
across runs—i.e., replace the current Select-Object -ExpandProperty FullName
-First 1 usage with a sorted selection (Sort-Object on LastWriteTime or FullName
followed by Select-Object -First 1) to ensure reproducible artifact choice.

Comment on lines +292 to +303
throw "WSL command was requested for python fallback but wsl executable is not available."
}

$converted = & $wsl.Source -e wslpath -a $Path 2>$null
$exitCode = Get-LastExitCodeOrZero
if ($exitCode -ne 0) {
throw "Failed to convert '$Path' into a WSL path using wslpath."
}

$text = ($converted | Out-String).Trim()
if ([string]::IsNullOrWhiteSpace($text)) {
throw "WSL path conversion produced no output for '$Path'."
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add classification codes to new WSL conversion failure throws.

These failure branches should expose stable reason prefixes for automation and repro triage.

🏷️ Proposed error-code prefixes
-        throw "WSL command was requested for python fallback but wsl executable is not available."
+        throw "PYTHON_WSL_UNAVAILABLE: WSL command was requested for python fallback but wsl executable is not available."
@@
-        throw "Failed to convert '$Path' into a WSL path using wslpath."
+        throw "PYTHON_WSLPATH_FAILED: Failed to convert '$Path' into a WSL path using wslpath."
@@
-        throw "WSL path conversion produced no output for '$Path'."
+        throw "PYTHON_WSLPATH_EMPTY: WSL path conversion produced no output for '$Path'."

As per coding guidelines "Failure conditions must use explicit classification codes".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
throw "WSL command was requested for python fallback but wsl executable is not available."
}
$converted = & $wsl.Source -e wslpath -a $Path 2>$null
$exitCode = Get-LastExitCodeOrZero
if ($exitCode -ne 0) {
throw "Failed to convert '$Path' into a WSL path using wslpath."
}
$text = ($converted | Out-String).Trim()
if ([string]::IsNullOrWhiteSpace($text)) {
throw "WSL path conversion produced no output for '$Path'."
throw "PYTHON_WSL_UNAVAILABLE: WSL command was requested for python fallback but wsl executable is not available."
}
$converted = & $wsl.Source -e wslpath -a $Path 2>$null
$exitCode = Get-LastExitCodeOrZero
if ($exitCode -ne 0) {
throw "PYTHON_WSLPATH_FAILED: Failed to convert '$Path' into a WSL path using wslpath."
}
$text = ($converted | Out-String).Trim()
if ([string]::IsNullOrWhiteSpace($text)) {
throw "PYTHON_WSLPATH_EMPTY: WSL path conversion produced no output for '$Path'."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/run-live-validation.ps1` around lines 292 - 303, Update the three throw
statements in the WSL path conversion block to include stable classification
codes so automation can parse failures: change the "wsl executable not
available" throw (near $wsl.Source check) to include a code like
"ERR-WSL-NOTFOUND:"; change the "Failed to convert ... using wslpath." throw
(after checking $exitCode from Get-LastExitCodeOrZero) to include a code like
"ERR-WSL-WSLPATH:" and include the exitCode variable where available; and change
the "WSL path conversion produced no output" throw (after checking $text) to
include a code like "ERR-WSL-NOOUTPUT:". Ensure each modified throw keeps the
original human-readable message after the code for context.

Comment on lines +328 to +337
$buildOutput = & $buildScript -Mode Windows -Configuration $Config
$buildExitCode = Get-LastExitCodeOrZero
if ($buildExitCode -ne 0) {
throw "ATTACH_NATIVE_HOST_PRECHECK_FAILED: native host build exited with code $buildExitCode."
}
foreach ($line in @($buildOutput)) {
if (-not [string]::IsNullOrWhiteSpace([string]$line)) {
Write-Information ([string]$line) -InformationAction Continue
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Emit captured native-build output before failing preflight.

At Line 331 the function throws before replaying $buildOutput, which removes the exact diagnostics needed for failure triage.

🛠️ Proposed ordering fix
     $buildOutput = & $buildScript -Mode Windows -Configuration $Config
     $buildExitCode = Get-LastExitCodeOrZero
-    if ($buildExitCode -ne 0) {
-        throw "ATTACH_NATIVE_HOST_PRECHECK_FAILED: native host build exited with code $buildExitCode."
-    }
     foreach ($line in @($buildOutput)) {
         if (-not [string]::IsNullOrWhiteSpace([string]$line)) {
             Write-Information ([string]$line) -InformationAction Continue
         }
     }
+    if ($buildExitCode -ne 0) {
+        throw "ATTACH_NATIVE_HOST_PRECHECK_FAILED: native host build exited with code $buildExitCode."
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$buildOutput = & $buildScript -Mode Windows -Configuration $Config
$buildExitCode = Get-LastExitCodeOrZero
if ($buildExitCode -ne 0) {
throw "ATTACH_NATIVE_HOST_PRECHECK_FAILED: native host build exited with code $buildExitCode."
}
foreach ($line in @($buildOutput)) {
if (-not [string]::IsNullOrWhiteSpace([string]$line)) {
Write-Information ([string]$line) -InformationAction Continue
}
}
$buildOutput = & $buildScript -Mode Windows -Configuration $Config
$buildExitCode = Get-LastExitCodeOrZero
foreach ($line in @($buildOutput)) {
if (-not [string]::IsNullOrWhiteSpace([string]$line)) {
Write-Information ([string]$line) -InformationAction Continue
}
}
if ($buildExitCode -ne 0) {
throw "ATTACH_NATIVE_HOST_PRECHECK_FAILED: native host build exited with code $buildExitCode."
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/run-live-validation.ps1` around lines 328 - 337, The script currently
throws on a non-zero $buildExitCode before replaying $buildOutput, losing
valuable build diagnostics; modify the control flow in the block that runs &
$buildScript (using $buildOutput and Get-LastExitCodeOrZero) so that you always
iterate over $buildOutput and call Write-Information for non-empty lines (the
foreach over @($buildOutput) with the IsNullOrWhiteSpace check) before throwing
the ATTACH_NATIVE_HOST_PRECHECK_FAILED error when $buildExitCode -ne 0; keep the
same diagnostic formatting and only throw after replaying the captured output.

@Prekzursil Prekzursil force-pushed the codex/m1-epic7-closure-phase branch from cc5f7e7 to 1620cc4 Compare February 28, 2026 22:46
Harden native host bootstrap and live runner host-path wiring, align promoted matrix set_unit_cap sequencing with fail-closed restore semantics, add forced-context ProcessLocator unit coverage, and update runbook/TODO evidence references for final live matrix runs.

Co-authored-by: Codex <noreply@openai.com>
@Prekzursil Prekzursil force-pushed the codex/m1-epic7-closure-phase branch from 1620cc4 to 6ed7199 Compare February 28, 2026 22:50
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tools/run-live-validation.ps1 (1)

85-165: Consider extracting shared helpers to a common module.

Invoke-CapturedCommand, Test-IsWslPythonCommand, and Convert-ToWslPath are duplicated between this file and tools/collect-mod-repro-bundle.ps1. Extracting to a shared module (e.g., tools/common/wsl-helpers.ps1) would reduce maintenance burden.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/run-live-validation.ps1` around lines 85 - 165, Multiple functions
(Invoke-CapturedCommand, Test-IsWslPythonCommand, Convert-ToWslPath) are
duplicated; extract them into a shared module (for example
tools/common/wsl-helpers.ps1) and update both scripts to load that module. Move
the full implementations of Invoke-CapturedCommand, Test-IsWslPythonCommand, and
Convert-ToWslPath into the new file, ensure they are declared as functions
exported or remain available for dot-sourcing, then replace the duplicated
definitions in tools/run-live-validation.ps1 and
tools/collect-mod-repro-bundle.ps1 with a single module import (dot-source or
Import-Module) and run the scripts to verify behavior and tests still pass.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tools/run-live-validation.ps1`:
- Around line 85-165: Multiple functions (Invoke-CapturedCommand,
Test-IsWslPythonCommand, Convert-ToWslPath) are duplicated; extract them into a
shared module (for example tools/common/wsl-helpers.ps1) and update both scripts
to load that module. Move the full implementations of Invoke-CapturedCommand,
Test-IsWslPythonCommand, and Convert-ToWslPath into the new file, ensure they
are declared as functions exported or remain available for dot-sourcing, then
replace the duplicated definitions in tools/run-live-validation.ps1 and
tools/collect-mod-repro-bundle.ps1 with a single module import (dot-source or
Import-Module) and run the scripts to verify behavior and tests still pass.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc5f7e7 and 1620cc4.

📒 Files selected for processing (8)
  • TODO.md
  • docs/LIVE_VALIDATION_RUNBOOK.md
  • tests/SwfocTrainer.Tests/Profiles/LiveHeroHelperWorkflowTests.cs
  • tests/SwfocTrainer.Tests/Profiles/LivePromotedActionMatrixTests.cs
  • tests/SwfocTrainer.Tests/Runtime/ProcessLocatorForcedContextTests.cs
  • tools/collect-mod-repro-bundle.ps1
  • tools/native/build-native.ps1
  • tools/run-live-validation.ps1
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/SwfocTrainer.Tests/Runtime/ProcessLocatorForcedContextTests.cs

@sonarqubecloud
Copy link

@Prekzursil
Copy link
Owner Author

Finalization update on commit 6ed7199:

  • Fresh local gate rerun passed (dotnet build, filtered dotnet test 216/0, workshop+seed schema validators, and strict repro-bundle validation for Tactical/AOTR/ROE).
  • Fresh synchronize run is fully green; enforce-pr-evidence now passes with required PR-body fields.
  • Required checks currently all passing; branch is blocked only by required review policy (REVIEW_REQUIRED).

Evidence bundle references remain:

  • TestResults/runs/LIVE-EAW-20260228-204540/eaw-process-snapshot.json
  • TestResults/runs/LIVE-TACTICAL-20260228-211256/repro-bundle.json
  • TestResults/runs/LIVE-AOTR-20260228-211521/repro-bundle.json
  • TestResults/runs/LIVE-ROE-20260228-211757/repro-bundle.json
  • TestResults/runs/LIVE-MATRIX-20260228-211757.md

@Prekzursil
Copy link
Owner Author

Finalization update on commit :\n\n- Fresh local gate rerun passed (MSBuild version 17.8.3+195e7f5a3 for .NET
Determining projects to restore...
All projects are up-to-date for restore.
SwfocTrainer.Meg -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Meg\bin\Debug\net8.0\SwfocTrainer.Meg.dll
SwfocTrainer.DataIndex -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.DataIndex\bin\Debug\net8.0\SwfocTrainer.DataIndex.dll
SwfocTrainer.Flow -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Flow\bin\Debug\net8.0\SwfocTrainer.Flow.dll
SwfocTrainer.Core -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Core\bin\Debug\net8.0\SwfocTrainer.Core.dll
SwfocTrainer.Helper -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Helper\bin\Debug\net8.0\SwfocTrainer.Helper.dll
SwfocTrainer.Catalog -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Catalog\bin\Debug\net8.0\SwfocTrainer.Catalog.dll
SwfocTrainer.Saves -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Saves\bin\Debug\net8.0\SwfocTrainer.Saves.dll
SwfocTrainer.Profiles -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Profiles\bin\Debug\net8.0\SwfocTrainer.Profiles.dll
SwfocTrainer.Runtime -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Runtime\bin\Debug\net8.0-windows\SwfocTrainer.Runtime.dll
SwfocTrainer.App -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.App\bin\Debug\net8.0-windows\SwfocTrainer.App.dll
SwfocTrainer.Tests -> C:\Users\Prekzursil\Downloads\SWFOC editor\tests\SwfocTrainer.Tests\bin\Debug\net8.0-windows\SwfocTrainer.Tests.dll

Build succeeded.
0 Warning(s)
0 Error(s)

Time Elapsed 00:00:03.73
, filtered Determining projects to restore...
All projects are up-to-date for restore.
SwfocTrainer.Meg -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Meg\bin\Debug\net8.0\SwfocTrainer.Meg.dll
SwfocTrainer.Core -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Core\bin\Debug\net8.0\SwfocTrainer.Core.dll
SwfocTrainer.Helper -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Helper\bin\Debug\net8.0\SwfocTrainer.Helper.dll
SwfocTrainer.DataIndex -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.DataIndex\bin\Debug\net8.0\SwfocTrainer.DataIndex.dll
SwfocTrainer.Profiles -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Profiles\bin\Debug\net8.0\SwfocTrainer.Profiles.dll
SwfocTrainer.Saves -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Saves\bin\Debug\net8.0\SwfocTrainer.Saves.dll
SwfocTrainer.Catalog -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Catalog\bin\Debug\net8.0\SwfocTrainer.Catalog.dll
SwfocTrainer.Runtime -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Runtime\bin\Debug\net8.0-windows\SwfocTrainer.Runtime.dll
SwfocTrainer.Flow -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.Flow\bin\Debug\net8.0\SwfocTrainer.Flow.dll
SwfocTrainer.App -> C:\Users\Prekzursil\Downloads\SWFOC editor\src\SwfocTrainer.App\bin\Debug\net8.0-windows\SwfocTrainer.App.dll
SwfocTrainer.Tests -> C:\Users\Prekzursil\Downloads\SWFOC editor\tests\SwfocTrainer.Tests\bin\Debug\net8.0-windows\SwfocTrainer.Tests.dll
Test run for C:\Users\Prekzursil\Downloads\SWFOC editor\tests\SwfocTrainer.Tests\bin\Debug\net8.0-windows\SwfocTrainer.Tests.dll (.NETCoreApp,Version=v8.0)
Microsoft (R) Test Execution Command Line Tool Version 17.8.0 (x64)
Copyright (c) Microsoft Corporation. All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:11.21] SwfocTrainer.Tests.Profiles.LiveCreditsTests.Credits_LiveDiagnostic_Should_Identify_Working_Strategy [SKIP]
Skipped SwfocTrainer.Tests.Profiles.LiveCreditsTests.Credits_LiveDiagnostic_Should_Identify_Working_Strategy [1 ms]
[xUnit.net 00:00:12.49] SwfocTrainer.Tests.Profiles.LiveRoeRuntimeHealthTests.Roe_Attach_And_Credits_Action_Should_Succeed_On_Live_Process [SKIP]
[xUnit.net 00:00:12.53] SwfocTrainer.Tests.Profiles.LivePromotedActionMatrixTests.Promoted_Actions_Should_Route_Via_Extender_Without_Hybrid_Fallback [SKIP]
[xUnit.net 00:00:12.67] SwfocTrainer.Tests.Profiles.LiveHeroHelperWorkflowTests.Hero_Helper_Workflow_Should_Return_Action_Result_For_Aotr_Or_Roe [SKIP]
[xUnit.net 00:00:12.70] SwfocTrainer.Tests.Profiles.LiveTacticalToggleWorkflowTests.Tactical_Toggles_Should_Execute_And_Revert_When_Tactical_Mode [SKIP]
Skipped SwfocTrainer.Tests.Profiles.LiveRoeRuntimeHealthTests.Roe_Attach_And_Credits_Action_Should_Succeed_On_Live_Process [1 ms]
Skipped SwfocTrainer.Tests.Profiles.LivePromotedActionMatrixTests.Promoted_Actions_Should_Route_Via_Extender_Without_Hybrid_Fallback [1 ms]
Skipped SwfocTrainer.Tests.Profiles.LiveHeroHelperWorkflowTests.Hero_Helper_Workflow_Should_Return_Action_Result_For_Aotr_Or_Roe [1 ms]
Skipped SwfocTrainer.Tests.Profiles.LiveTacticalToggleWorkflowTests.Tactical_Toggles_Should_Execute_And_Revert_When_Tactical_Mode [1 ms]

Passed! - Failed: 0, Passed: 218, Skipped: 5, Total: 223, Duration: 12 s - SwfocTrainer.Tests.dll (net8.0)
216/0, workshop+seed schema validators, and strict repro-bundle validation for Tactical/AOTR/ROE).\n- Fresh synchronize run is fully green; now passes with required PR-body fields.\n- Required checks currently all passing; branch is blocked only by required review policy ().\n\nEvidence bundle references remain:\n- \n- \n- \n- \n-

@Prekzursil Prekzursil merged commit 7e6f2a9 into main Feb 28, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:high High-risk change; strict human gate required.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant