1 Commits

Author SHA1 Message Date
gamer147
3a076e2781 Constrain core validation memory usage
Some checks failed
Core validation / Linux core gate (push) Successful in 55s
Release builds / Linux x64 artifact (push) Failing after 39s
Release builds / Windows x64 artifact (push) Failing after 48s
Release builds / Publish tagged Gitea release (push) Has been skipped
2026-08-18 09:00:42 -04:00
3 changed files with 72 additions and 6 deletions

View File

@@ -26,7 +26,7 @@ whenever a tool's inputs/outputs change.**
| Tool | Purpose | Run | Reads → Writes | | Tool | Purpose | Run | Reads → Writes |
|---|---|---|---| |---|---|---|---|
| `validate.py` | Layered project validation front door. `core` regenerates/runtime-checks opcode metadata, lints canonical registries, runs pure Python tooling tests and the 502 .NET cases without the `Workspace` trait, checks generated opcode references, and runs `git diff --check`. `workspace` adds the 88 explicitly traited installed-data/native-oracle cases, corpus-derived global generation, real-data Python suites, full SYS4 decode, and Python RECOVER. `runtime` adds the Godot C# build and forced-portable threaded self-test. `full` combines all phases and adds the booted faithful-wait C# scene sweep. Selected prerequisites are strict: an unavailable game/corpus/Godot requirement fails before execution instead of becoming a green skip. Each gate has a timeout and UTF-8 log under `build/validation/validate-<timestamp>/`; Godot receives an isolated validation-owned user-data/log root there so it cannot read or modify the developer's saves/settings. The final table reports results/durations and a before/after Godot-process leak audit. | `validate.py` (defaults to `--level full`) · `--level core|workspace|runtime|full` · `--godot <console>` · `--game-root <install>` · `--verbose` · `--fail-fast` | sources + selected toolchain/game/corpus prerequisites → console summary + ⚙ `build/validation/validate-*/<gate>.log` | | `validate.py` | Layered project validation front door. `core` regenerates/runtime-checks opcode metadata, lints canonical registries, runs pure Python tooling tests and the source-only .NET cases without the `Workspace` trait, checks generated opcode references, and runs `git diff --check`. Its .NET solution restore, CLI build, and test-project execution are separate low-memory gates; restore/build parallelism, compiler/MSBuild servers, server GC, and debug-symbol emission are disabled so constrained hosted runners do not kill Roslyn with exit 137. `workspace` adds the explicitly traited installed-data/native-oracle cases, corpus-derived global generation, real-data Python suites, full SYS4 decode, and Python RECOVER. `runtime` adds the Godot C# build and forced-portable threaded self-test. `full` combines all phases and adds the booted faithful-wait C# scene sweep. Selected prerequisites are strict: an unavailable game/corpus/Godot requirement fails before execution instead of becoming a green skip. Each gate has a timeout and UTF-8 log under `build/validation/validate-<timestamp>/`; Godot receives an isolated validation-owned user-data/log root there so it cannot read or modify the developer's saves/settings. The final table reports results/durations and a before/after Godot-process leak audit. | `validate.py` (defaults to `--level full`) · `--level core|workspace|runtime|full` · `--godot <console>` · `--game-root <install>` · `--verbose` · `--fail-fast` | sources + selected toolchain/game/corpus prerequisites → console summary + ⚙ `build/validation/validate-*/<gate>.log` |
| `test_validate.py` | Pure tests for launcher-equivalent explicit/environment/PATH/conventional resolution precedence, invalid-explicit hard failure, level composition, and final gate ordering. | `test_validate.py` | temporary files only | | `test_validate.py` | Pure tests for launcher-equivalent explicit/environment/PATH/conventional resolution precedence, invalid-explicit hard failure, level composition, and final gate ordering. | `test_validate.py` | temporary files only |
Levels are cumulative around `core`: `workspace` means core+workspace-corpus, `runtime` means core+Godot, Levels are cumulative around `core`: `workspace` means core+workspace-corpus, `runtime` means core+Godot,
@@ -45,7 +45,8 @@ access; failure logs are retained for seven days. The private remote's default a
`develop`; pushes and pull requests targeting it select the gate. The rewritten repository is published there, `develop`; pushes and pull requests targeting it select the gate. The rewritten repository is published there,
and the first actual Linux/Gitea core run succeeded on 2026-08-03 before the later source-history sanitation; and the first actual Linux/Gitea core run succeeded on 2026-08-03 before the later source-history sanitation;
its tree-equivalent rewritten commit is `524ea74`. The hosted gate also succeeded on the sanitized lineage at its tree-equivalent rewritten commit is `524ea74`. The hosted gate also succeeded on the sanitized lineage at
`d673652` on 2026-08-03. `d673652` on 2026-08-03. The same driver now owns the constrained-memory .NET environment rather than relying on
workflow-only settings, so local and hosted core/workspace runs share the Roslyn memory contract.
The artifact wrapper is `.gitea/workflows/linux-release-build.yml`. It runs independently from the fast core The artifact wrapper is `.gitea/workflows/linux-release-build.yml`. It runs independently from the fast core
gate on `develop` pushes, manual dispatch, and `v*` tag pushes. Independent Linux and Windows jobs use the same gate on `develop` pushes, manual dispatch, and `v*` tag pushes. Independent Linux and Windows jobs use the same

View File

@@ -70,6 +70,8 @@ class EnvironmentResolutionTests(unittest.TestCase):
class GatePlanTests(unittest.TestCase): class GatePlanTests(unittest.TestCase):
def test_core_contains_no_workspace_or_runtime_gates(self) -> None: def test_core_contains_no_workspace_or_runtime_gates(self) -> None:
keys = {gate.key for gate in validate.build_gate_plan("core")} keys = {gate.key for gate in validate.build_gate_plan("core")}
self.assertIn("engine-restore", keys)
self.assertIn("engine-cli-build", keys)
self.assertIn("engine-tests", keys) self.assertIn("engine-tests", keys)
self.assertIn("python-test_validate", keys) self.assertIn("python-test_validate", keys)
self.assertNotIn("sys4-corpus-validate", keys) self.assertNotIn("sys4-corpus-validate", keys)
@@ -77,6 +79,26 @@ class GatePlanTests(unittest.TestCase):
self.assertNotIn("age-cli-sweep", keys) self.assertNotIn("age-cli-sweep", keys)
engine = next(gate for gate in validate.build_gate_plan("core") if gate.key == "engine-tests") engine = next(gate for gate in validate.build_gate_plan("core") if gate.key == "engine-tests")
self.assertIn("Category!=Workspace", engine.command) self.assertIn("Category!=Workspace", engine.command)
self.assertIn("--no-restore", engine.command)
self.assertIn("-m:1", engine.command)
self.assertIn("-p:BuildInParallel=false", engine.command)
self.assertEqual(validate.LOW_MEMORY_DOTNET_ENVIRONMENT, engine.environment)
self.assertIn(
"engine/Age.Engine.Tests/Age.Engine.Tests.csproj",
engine.command,
)
cli = next(
gate for gate in validate.build_gate_plan("core") if gate.key == "engine-cli-build"
)
self.assertIn("engine/Age.Cli/Age.Cli.csproj", cli.command)
self.assertIn("--no-restore", cli.command)
self.assertIn("-m:1", cli.command)
self.assertEqual(validate.LOW_MEMORY_DOTNET_ENVIRONMENT, cli.environment)
restore = next(
gate for gate in validate.build_gate_plan("core") if gate.key == "engine-restore"
)
self.assertIn("--disable-parallel", restore.command)
self.assertEqual(validate.LOW_MEMORY_DOTNET_ENVIRONMENT, restore.environment)
def test_workspace_extends_core(self) -> None: def test_workspace_extends_core(self) -> None:
core = {gate.key for gate in validate.build_gate_plan("core")} core = {gate.key for gate in validate.build_gate_plan("core")}
@@ -90,6 +112,9 @@ class GatePlanTests(unittest.TestCase):
if gate.key == "engine-workspace-tests" if gate.key == "engine-workspace-tests"
) )
self.assertIn("Category=Workspace", installed.command) self.assertIn("Category=Workspace", installed.command)
self.assertIn("--no-restore", installed.command)
self.assertIn("-m:1", installed.command)
self.assertEqual(validate.LOW_MEMORY_DOTNET_ENVIRONMENT, installed.environment)
def test_runtime_requires_resolved_paths(self) -> None: def test_runtime_requires_resolved_paths(self) -> None:
with self.assertRaisesRegex(ValueError, "resolved Godot"): with self.assertRaisesRegex(ValueError, "resolved Godot"):

View File

@@ -55,6 +55,12 @@ WORKSPACE_GENERATED_INPUTS = (
paths.BUILD / "callscript-names.json", paths.BUILD / "callscript-names.json",
paths.BUILD / "scjump-decisions.json", paths.BUILD / "scjump-decisions.json",
) )
LOW_MEMORY_DOTNET_ENVIRONMENT = (
("DOTNET_CLI_USE_MSBUILD_SERVER", "0"),
("MSBUILDDISABLENODEREUSE", "1"),
("DOTNET_gcServer", "0"),
("DOTNET_GCConserveMemory", "9"),
)
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -156,15 +162,45 @@ def build_gate_plan(
) )
for test in CORE_TESTS for test in CORE_TESTS
) )
gates.append(
Gate(
"engine-restore",
".NET engine restore",
(
"dotnet", "restore", "engine/AgeEngine.sln", "--nologo",
"--verbosity", "minimal", "--disable-parallel",
"-p:RestoreDisableParallel=true",
),
300,
LOW_MEMORY_DOTNET_ENVIRONMENT,
)
)
gates.append(
Gate(
"engine-cli-build",
".NET engine CLI build",
(
"dotnet", "build", "engine/Age.Cli/Age.Cli.csproj", "--no-restore", "--nologo",
"--verbosity", "minimal", "-m:1", "-p:UseSharedCompilation=false",
"-p:BuildInParallel=false", "-p:DebugType=None", "-p:DebugSymbols=false",
),
300,
LOW_MEMORY_DOTNET_ENVIRONMENT,
)
)
gates.append( gates.append(
Gate( Gate(
"engine-tests", "engine-tests",
".NET engine core tests", ".NET engine core tests",
( (
"dotnet", "test", "engine/AgeEngine.sln", "--nologo", "--verbosity", "minimal", "dotnet", "test", "engine/Age.Engine.Tests/Age.Engine.Tests.csproj",
"--filter", "Category!=Workspace", "-p:UseSharedCompilation=false", "--no-restore", "--nologo",
"--verbosity", "minimal", "--filter", "Category!=Workspace", "-m:1",
"-p:UseSharedCompilation=false", "-p:BuildInParallel=false",
"-p:DebugType=None", "-p:DebugSymbols=false",
), ),
300, 300,
LOW_MEMORY_DOTNET_ENVIRONMENT,
) )
) )
@@ -174,10 +210,14 @@ def build_gate_plan(
"engine-workspace-tests", "engine-workspace-tests",
".NET installed-data and native-oracle tests", ".NET installed-data and native-oracle tests",
( (
"dotnet", "test", "engine/AgeEngine.sln", "--nologo", "--verbosity", "minimal", "dotnet", "test", "engine/Age.Engine.Tests/Age.Engine.Tests.csproj",
"--filter", "Category=Workspace", "-p:UseSharedCompilation=false", "--no-restore", "--nologo",
"--verbosity", "minimal", "--filter", "Category=Workspace", "-m:1",
"-p:UseSharedCompilation=false", "-p:BuildInParallel=false",
"-p:DebugType=None", "-p:DebugSymbols=false",
), ),
300, 300,
LOW_MEMORY_DOTNET_ENVIRONMENT,
) )
) )
gates.append( gates.append(