Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4ee606f
Update PurpleWave source and rebase its runtime isolation patch.
coin-au-carre Oct 25, 2026
baf9e60
Record PurpleWave candidate build validation.
coin-au-carre Oct 25, 2026
ea7c718
Pin PurpleWave to the revision before the Aim regression.
coin-au-carre Oct 25, 2026
f2748a6
Record the successful PurpleWave parent build and packaging blocker.
coin-au-carre Oct 25, 2026
0af3074
Archive overlapping PurpleWave recipe patches only once.
coin-au-carre Oct 25, 2026
33fede6
Record PurpleWave review packaging and SB gameplay checks.
coin-au-carre Oct 25, 2026
b442eb3
Record PurpleWave full-match and persistence validation.
coin-au-carre Oct 26, 2026
3057941
Record PurpleWave bridge and native runtime review.
coin-au-carre Oct 26, 2026
4bd5633
Guard PurpleWave gathering against empty melee threats.
coin-au-carre Oct 26, 2026
3b5c533
Record PurpleWave Gather regression and match evidence.
coin-au-carre Oct 26, 2026
6d1875d
Verify PurpleWave x64 play and saved history reload.
coin-au-carre Oct 26, 2026
467e656
Approve scoped PurpleWave sb.6 source and distribution review.
coin-au-carre Oct 27, 2026
8a383bb
Record PurpleWave installer checks and Terran bridge blockers.
coin-au-carre Oct 27, 2026
c6b968b
Approve scoped PurpleWave sb.6 production release proposal.
coin-au-carre Oct 27, 2026
f31368c
Record passing PurpleWave release-unit checks.
coin-au-carre Oct 27, 2026
fd0985f
Use project attribution in PurpleWave release metadata.
coin-au-carre Oct 27, 2026
17b377c
Keep PurpleWave sb.6 experimental pending client rollout.
coin-au-carre Oct 27, 2026
1b5f02f
Reconcile PurpleWave sb.6 experimental staging package.
coin-au-carre Oct 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions bots/purplewave/BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,17 @@ application arguments. JNA and the JVM also use memory outside the Java heap.
Use the recipe revision recorded in a published package when reproducing that release.
The TypeScript recipes on `main` require a fresh build directory and a new release ID;
older `.mjs` build records and published archives are not rewritten by this migration.

The sb.6 source patch guards Gather's melee escape predicate against an empty
threat list. This fixes an exception for zero-health worker snapshots; nonempty
threat damage and goal scoring retain their upstream behavior. Patch details and
review scope are in `patches/purplewave/0002-guard-empty-gather-threats.md`.

After building, run the compiled-code regression from the repository root:

```bat
bots\purplewave\runtime-tests\run-gather-tests.cmd <java-21-home> .build\<build-name>
```

The regression invokes the actual Scala 2.12 local lazy predicate with synthetic
worker health and empty strict/lazy collections. It needs no live BWAPI process.
4 changes: 4 additions & 0 deletions bots/purplewave/RELEASE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ This is a modified build maintained by ShieldBattery, not an unmodified upstream
binary or an endorsement by its authors. Original copyright/license notices are
included beside this file and with the source.

Changes dated 2026-10-26:
- Skip melee escape scoring when no eligible melee threats exist, including
zero-health worker snapshots. Other escape scoring stays unchanged.

Changes dated 2026-09-22:
- Use the assigned working profile and fixed ShieldBattery configuration.
- Encode opponent filenames and history cells; disable visualizer auto-launch.
Expand Down
20 changes: 11 additions & 9 deletions bots/purplewave/bot.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
"formats": [
{
"id": "one-v-one",
"support": "verified",
"notes": "All three upstream races are enabled. Protoss completed a local x64 match and x86 smoke tests; Terran and Zerg have not yet had dedicated SC:R bridge game tests. https://github.com/ShieldBattery/robotics-facility/blob/purplewave-sb-3/docs/releases/purplewave-sb-3.md"
"support": "experimental",
"notes": "Experimental local play with assigned Protoss, Terran or Zerg. Reviewed gameplay used a compatible ShieldBattery client; broader map, command and rollout coverage remains unverified. See docs/releases/purplewave-sb-6-admission.md."
},
{
"id": "teams",
Expand All @@ -52,7 +52,7 @@
}
],
"mapConstraints": [
"Legacy SC:R unit limits required by the experimental bridge."
"Legacy SC:R unit limits required by the BWAPI bridge."
],
"learning": {
"mode": "persistent",
Expand All @@ -68,7 +68,7 @@
"permissions": {
"localDistribution": {
"status": "approved",
"evidence": "Prior local distribution review plus native dependency notice correction: https://github.com/ShieldBattery/robotics-facility/blob/purplewave-sb-4/docs/releases/purplewave-sb-4.md"
"evidence": "Scoped source, runtime effects, source-delivery and complete notice review of exact sb.6 build. Public competition remains unreviewed. docs/releases/purplewave-sb-6-admission.md"
},
"publicCompetition": {
"status": "unreviewed",
Expand All @@ -84,20 +84,22 @@
"x86",
"x86_64"
],
"evidence": "https://github.com/ShieldBattery/robotics-facility/blob/purplewave-sb-2/docs/releases/purplewave-sb-2.md",
"evidence": "docs/releases/purplewave-sb-6-admission.md",
"limitations": [
"Experimental partial BWAPI semantics.",
"Terran and Zerg are enabled from upstream support; dedicated SC:R bridge game verification is pending. Other game formats remain unverified."
"Experimental local 1v1 with assigned Protoss, Terran or Zerg only. Random, teams, FFA, unusual maps and exhaustive late-game command coverage remain unverified.",
"Requires reviewed gathered-resource reporting, training cancellation, building-flight/Spider Mines commands and supervised release practice in the ShieldBattery client.",
"Historical release-DLL gameplay and saved-state checks apply to their recorded client/runtime combination. Production approval is deferred until client rollout and signed staging delivery are verified.",
"Scoped source/runtime review is not an OS sandbox. Human difficulty and public competition remain unreviewed."
]
},
"build": {
"status": "recipe-in-robotics-facility",
"repository": "https://github.com/ShieldBattery/robotics-facility.git",
"revision": "bac6f6b1c26a9b99e06c22b08f311561ee35321b",
"revision": "17b377ca0d5df068e52abb5bdcf57867d697477f",
"instructions": "bots/purplewave/BUILD.md"
},
"sourceReview": {
"status": "approved",
"evidence": "Prior source review and unchanged executable bytes; native dependency notice correction: https://github.com/ShieldBattery/robotics-facility/blob/purplewave-sb-4/docs/releases/purplewave-sb-4.md"
"evidence": "Pinned source/runtime effects and corresponding-source delivery reviewed for scoped experimental local play. Archive-specific checks and historical runtime evidence are recorded in docs/releases/purplewave-sb-6-admission.md."
}
}
35 changes: 35 additions & 0 deletions bots/purplewave/runtime-tests/GatherEmptyThreatsTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package Micro

import Micro.Actions.Basic.Gather
import ProxyBwapi.UnitInfo.FriendlyUnitInfo
import scala.runtime.{LazyBoolean, LazyRef}

object GatherEmptyThreatsTest extends App {
val unsafeField = classOf[sun.misc.Unsafe].getDeclaredField("theUnsafe")
unsafeField.setAccessible(true)
val unsafe = unsafeField.get(null).asInstanceOf[sun.misc.Unsafe]
val unit = unsafe.allocateInstance(classOf[FriendlyUnitInfo]).asInstanceOf[FriendlyUnitInfo]
def set(name: String, value: Int): Unit = {
val field = Iterator.iterate[Class[_]](unit.getClass)(_.getSuperclass)
.takeWhile(_ != null).flatMap(_.getDeclaredFields).find(f => f.getName == name || f.getName.endsWith("$$" + name)).get
field.setAccessible(true)
field.setInt(unit, value)
}

// Invoke the actual Scala 2.12 local lazy predicate without starting BWAPI.
val predicate = Gather.getClass.getDeclaredMethods
.find(_.getName.startsWith("stabbingLethal$lzycompute$")).get
predicate.setAccessible(true)
var passed = 0
for (health <- Seq(0, -1, 1, 40); view <- Seq(false, true)) {
set("_hitPoints", health)
val threats = new LazyRef[Seq[ProxyBwapi.UnitInfo.UnitInfo]]
val empty = Vector.empty[ProxyBwapi.UnitInfo.UnitInfo]
threats.initialize(if (view) empty.view else empty)
val lethal = predicate.invoke(null, new LazyBoolean, unit, threats).asInstanceOf[Boolean]
assert(!lethal, s"empty threats must not be lethal: health=$health view=$view")
passed += 1
println(s"PASS empty threats: health=$health view=$view")
}
println(s"$passed Gather empty-threat regressions passed")
}
16 changes: 16 additions & 0 deletions bots/purplewave/runtime-tests/run-gather-tests.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@echo off
setlocal
if "%~2"=="" (
echo Usage: run-gather-tests.cmd ^<java-21-home^> ^<build-directory^>
exit /b 2
)
set "JAVA=%~1\bin\java.exe"
set "BUILD=%~f2"
set "ROOT=%~dp0..\..\.."
set "OUTPUT=%BUILD%\gather-tests"
if not exist "%OUTPUT%" mkdir "%OUTPUT%"
set "CP=%ROOT%\.build\java-dependencies\*;%BUILD%\bin\PurpleWave.jar;%BUILD%\bin\lib\*"
"%JAVA%" -cp "%CP%" scala.tools.nsc.Main -usejavacp -d "%OUTPUT%" "%~dp0GatherEmptyThreatsTest.scala"
if errorlevel 1 exit /b %ERRORLEVEL%
"%JAVA%" -cp "%OUTPUT%;%CP%" Micro.GatherEmptyThreatsTest
exit /b %ERRORLEVEL%
126 changes: 126 additions & 0 deletions docs/releases/purplewave-sb-6-admission.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# PurpleWave sb.6 experimental staging admission

sb.6 remains **experimental local 1v1**, with assigned Protoss, Terran or Zerg.
Source/local-distribution review is approved for the pinned configuration and
recorded source/runtime boundaries. **Production approval is deferred.** Public
competition, human difficulty, Random, teams, FFA, unusual maps and exhaustive
late-game command conformance remain unverified.

## Corrected unpublished package

The prior local installer archive and edited production fragment no longer agreed:
the fragment changed downstream attribution while retaining the old descriptor and
ZIP digests. The corrected archive is built and packaged through maintained guarded
tooling from committed recipe `17b377ca0d5df068e52abb5bdcf57867d697477f`.
It records `ShieldBattery` as downstream modifier and experimental 1v1 in both
embedded descriptor and [staging fragment](purplewave-sb-6-staging-catalog.json).
The archive verifier checks descriptor equality, digest, entry sizes, CRCs, paths
and required files.

- Corrected ZIP SHA-256: `477ee3cc900eb0851bcd726c8e2f8781579a38daaf8c0f876383b5127f0db722`.
- ZIP size: 20,065,830 bytes; 1,186 unique entries.
- Embedded descriptor SHA-256: `18362afc51831f3bd78135edbf42bcbee4fec5960715811ecce4f2e5cef1d6f0`.
- Bot JAR SHA-256: `e2bfd2519e07e6597bd2159b6044df6c2fca11ebc2b86a532f28e182bd452013`.

All six runtime JARs, patched upstream source, notices and working configuration
are byte-identical to the prior admitted archive
`60f80501b9d898b0b392df624e35d15df4bcb3749338e77b8e78820405e980b4`.
Only `package.json`, `source/build-info.json` and
`source/tools/package-purplewave.ts` change. No members are added or removed.
Original local archives remain preserved by their original digests; no published
archive or historical catalog descriptor is overwritten.

The sb.6 release endpoint returned 404 during reconciliation, and the public release
inventory contained PurpleWave sb.1-sb.4 only. The retained sb.6 ID identifies a
corrected **unpublished** candidate, not a replacement for an immutable published
release. Upload exact reviewed bytes before adding its fragment to active catalog.
The current `catalog/catalog.json` remains unchanged while that URL is unavailable.

Fresh checks are recorded in
[pinned staging validation](purplewave-sb-6-staging-validation.json). Earlier
measurements are preserved separately in
[historical evidence](purplewave-sb-6-history.json), with their original archive,
client, architecture and toolchain scope. They are not new tests of this ZIP.

## Player-facing update

The source moves from `a57d2511cc4f6318c2a2d61a504e8f58a7b090af` to
`08672150433a2c60f5db23e10d2bc15dbc6e302e`, a 172-commit upstream update.
It includes PvP opening/defense changes, Dark Templar targeting, Observer survival
and scouting fixes, and pathfinding/combat-simulation performance work. These are
source changes, not a measured playing-strength or performance improvement for this
package. The local Gather guard prevents an empty-threat scoring exception for
zero-health worker snapshots; nonempty threat weighting and goal scoring stay
unchanged. [Patch rationale](../../patches/purplewave/0002-guard-empty-gather-threats.md)
records the compiled boundary case.

## Source, runtime effects and distribution

Both hashed patches apply to the exact source pin: runtime/storage isolation and
the nonempty-threat Gather guard. The review covers the full upstream delta,
patched source and fixed configuration, dependencies, build tools and delivered
notices. Upstream head after the selected pin contained unresolved Aim references;
this package does not include speculative combat workarounds.

The update adds fixed `bwapi-data/AI/fixedbuilds-by-opponent.json` input, with
opponent names selecting JSON keys rather than constructing paths; fallback remains
fixed `fixedbuilds.txt`. Placement-cache format changes from 3 to 5 and old caches
are regenerated. Opponent-history format v5 and its read/write deduplication remain
compatible. Source/runtime evidence for these paths is retained at original scope
in the historical record.

Fixed configuration disables human, ladder, livestream, tournament, pretraining,
debug and visualization modes. External visualizer auto-launch remains removed.
Active effects are package/config reads, assigned BWAPI shared-memory/named-pipe
IPC, encoded opponent histories, versioned placement caches and logs below isolated
app-owned profiles. Optional mjson URL/schema APIs and standalone research entrypoints
are not invoked by packaged main path. Gather guard adds no file, network or
process operation. Prior scoped WPR/JFR observations retain their attribution and
coverage limits; this is neither an OS sandbox nor a complete syscall trace.

Package preserves original PurpleWave MIT notice and all component notices,
including Java BWEM, JBWEB, JavaJPS, mjson, Scala, JNA/JNA Platform, Commons Lang and
native libffi. Corresponding patched sources, locks, patches and offline build
materials are included. Java is detected separately and is not bundled. The source
build invokes pinned Java/Scala compilers directly, without upstream Maven plugins,
Python helpers or launch scripts. Original author attribution remains intact;
source/local redistribution approval grants no public-competition endorsement.

## Historical gameplay and client requirements

Recorded runtime tests used the byte-identical six-JAR runtime with the exact
clients/DLLs in historical evidence. They include natural Protoss/Zerg and
Terran/Zerg outcomes on x86/x64, winner/frame agreement in real saved rows, later
history reload/deduplication, isolated simultaneous profiles and owned-process
cleanup. Bounded factory games observed lifted Barracks, Vultures, Siege Tanks
and actual Spider Mine units; separate fixtures exercised Lift and Land. Bounded
stops, manual concessions and fixture behavior are not natural full-match results.

Earlier clients rejected Lift and Spider Mines. Separate client fixes corrected
those paths in recorded release-DLL integration; those successful observations do
not establish support in every shipped client. Rejection counts and limitations
remain in original records. The corrected archive does not acquire a broader
verification claim from unchanged JARs.

Required client behavior includes gathered-resource reporting, training cancellation,
building-flight/Spider Mine translation, supervised release practice, signature and
archive verification, isolated writable profiles and lease-aware reset. The current
`minimumBridgeVersion` field is descriptive and is not enforced by the client;
bumping its string alone would not gate installation on these fixes.

## Staging and production rollout

No production approval field or standalone production proposal remains. Candidate
and package keep experimental 1v1. The fragment has no `releaseStatus`; the separate
production gate excludes such entries before download. Production approval needs a
later reviewed change after required client rollout and exact signed staging
artifact delivery are verified.

Experimental catalog admission also requires the per-release production gate from
[PR #4](https://github.com/ShieldBattery/robotics-facility/pull/4). A publisher without
that gate must not promote this candidate merely because source review is approved.
After gate integration, maintainers host this exact ZIP at its versioned artifact
URL, then merge the matching experimental fragment and publish a new signed
staging revision through existing workflow. Availability is not inferred from this
local package. No release upload, catalog signing with production keys, workflow
dispatch, public delivery or production promotion is part of this reconciliation.
Loading