diff --git a/.github/workflows/headless-portability.yml b/.github/workflows/headless-portability.yml
index 6cea0898..a24ae829 100644
--- a/.github/workflows/headless-portability.yml
+++ b/.github/workflows/headless-portability.yml
@@ -5,6 +5,7 @@ on:
paths:
- ".github/workflows/headless-portability.yml"
- "AcDream.slnx"
+ - "src/AcDream.Platform/**"
- "src/AcDream.Core/**"
- "src/AcDream.Core.Net/**"
- "src/AcDream.Content/**"
@@ -13,6 +14,7 @@ on:
- "src/AcDream.Headless/**"
- "src/AcDream.App/**"
- "src/AcDream.UI.Abstractions/**"
+ - "tests/AcDream.Platform.Tests/**"
- "tests/AcDream.Core.Tests/**"
- "tests/AcDream.Core.Net.Tests/**"
- "tests/AcDream.Content.Tests/**"
@@ -26,6 +28,7 @@ on:
paths:
- ".github/workflows/headless-portability.yml"
- "AcDream.slnx"
+ - "src/AcDream.Platform/**"
- "src/AcDream.Core/**"
- "src/AcDream.Core.Net/**"
- "src/AcDream.Content/**"
@@ -34,6 +37,7 @@ on:
- "src/AcDream.Headless/**"
- "src/AcDream.App/**"
- "src/AcDream.UI.Abstractions/**"
+ - "tests/AcDream.Platform.Tests/**"
- "tests/AcDream.Core.Tests/**"
- "tests/AcDream.Core.Net.Tests/**"
- "tests/AcDream.Content.Tests/**"
@@ -78,6 +82,7 @@ jobs:
shell: pwsh
run: |
$projects = @(
+ "src/AcDream.Platform/AcDream.Platform.csproj",
"src/AcDream.Plugin.Abstractions/AcDream.Plugin.Abstractions.csproj",
"src/AcDream.Core/AcDream.Core.csproj",
"src/AcDream.Core.Net/AcDream.Core.Net.csproj",
@@ -98,6 +103,7 @@ jobs:
shell: pwsh
run: |
$projects = @(
+ "tests/AcDream.Platform.Tests/AcDream.Platform.Tests.csproj",
"tests/AcDream.Core.Net.Tests/AcDream.Core.Net.Tests.csproj",
"tests/AcDream.Content.Tests/AcDream.Content.Tests.csproj",
"tests/AcDream.Runtime.Tests/AcDream.Runtime.Tests.csproj",
diff --git a/docs/architecture/acdream-architecture.md b/docs/architecture/acdream-architecture.md
index 2eae6a17..b74d6419 100644
--- a/docs/architecture/acdream-architecture.md
+++ b/docs/architecture/acdream-architecture.md
@@ -266,14 +266,21 @@ src/
World/
RuntimeWorldEnvironmentState.cs -> canonical calendar/time/weather owner
RuntimeWorldTransitState.cs -> canonical reveal generation/readiness owner
- Platform/
- ApplicationPathSet.cs -> shared BCL-only XDG/Windows config, data,
- cache, plugin, screenshot, and diagnostic paths
RuntimeGenerationReset.cs -> one retryable canonical-generation reset
-> Slice J complete; graphical and no-window hosts share one GameRuntime
- -> may reference Core, Core.Net, Content, and Plugin.Abstractions only
+ -> may reference Core, Core.Net, Content, Plugin.Abstractions, and
+ Platform only
-> must never reference App, UI, Silk.NET, OpenAL, or Arch
+ AcDream.Platform/ BCL-only portable path contract (Campaign LA LA0)
+ ApplicationPathSet.cs -> shared XDG/Windows config, data, cache,
+ plugin, screenshot, and diagnostic paths
+ -> zero project/package references (guarded by
+ tests/AcDream.Platform.Tests/PlatformDependencyBoundaryTests.cs);
+ Runtime references it and re-exports transitively to App/Headless;
+ the external launcher (AcDream.Launcher.Core) references ONLY this
+ project from the game solution
+
AcDream.Headless/ Linux/Windows no-window production host
Program.cs -> CLI entry only
Configuration/ -> strict versioned process/session config
diff --git a/docs/plans/2026-08-14-launcher-campaign.md b/docs/plans/2026-08-14-launcher-campaign.md
index ecb3620b..35728f64 100644
--- a/docs/plans/2026-08-14-launcher-campaign.md
+++ b/docs/plans/2026-08-14-launcher-campaign.md
@@ -89,12 +89,16 @@ Recon facts (2026-08-14): blast radius is the definition, six source files
`App/Program.cs`, `GameWindow.cs:533`, `HeadlessPathSet.cs`,
`HeadlessPlatformEnvironment.cs`; two more files are doc-comment-only), two
test files (`ApplicationPathSetTests.cs` moves to a new
-`tests/AcDream.Platform.Tests/` or stays keyed to the new assembly;
-`GraphicalLegacyConfigurationMigratorTests.cs` fixtures), and ONE dependency
-guard: `tests/AcDream.Headless.Tests/HeadlessDependencyBoundaryTests.cs`
-`HeadlessAssemblyReferencesOnlyTheRuntimeProject` asserts Headless references
-exactly `[AcDream.Runtime.csproj]` — amend to the exact new set in the same
-commit (deliberate, never silent). Namespace stays `AcDream.Runtime.Platform`?
+`tests/AcDream.Platform.Tests/`;
+`GraphicalLegacyConfigurationMigratorTests.cs` fixtures), and the dependency
+guards — CORRECTED post-review (the original recon here asserted the wrong
+guard, the C4-closeout failure mode): the K0 Headless guard
+(`HeadlessAssemblyReferencesOnlyTheRuntimeProject`) asserts HEADLESS's own
+csproj reference list, which this move does not touch — it stays UNCHANGED;
+the guard that actually needs amending is Runtime's own
+`RuntimeDependencyBoundaryTests.RuntimeProjectDeclaresOnlyApprovedProjectDependencies`
+(Runtime gains the `AcDream.Platform` reference), amended with a cited
+comment in the same commit. Namespace stays `AcDream.Runtime.Platform`?
NO — rename to `AcDream.Platform` and fix the eight usings (clean naming beats
avoiding a mechanical edit). Register new projects in `AcDream.slnx`.
@@ -400,10 +404,10 @@ LA6 adds CH-regression scrutiny; LA0 adds guard-integrity scrutiny.
| Slice | Status | Commits | Review | Notes |
|---|---|---|---|---|
-| LA0 | — | | | |
-| LA1 | — | | | |
+| LA0 | review PASS; fix round applied | `cb6502c8` + fixes | Opus PASS w/ 6 findings 2026-08-14; narrow re-review pending | Byte-identity proven; CI Linux lanes + arch doc + Platform self-guard + App explicit ref fixed; plan recon corrected |
+| LA1 | in flight (Sonnet) | | | pinned contract v1 + 5 optional fields |
| LA2 | — | | | |
-| LA3 | — | | | |
+| LA3 | in flight (Sonnet, isolated worktree) | | | pinned contract shared with LA1 |
| LA4 | — | | | |
| LA5 | — | | | |
| LA6 | — | | | |
diff --git a/memory/project_linux_graphical.md b/memory/project_linux_graphical.md
index a104e378..df7114de 100644
--- a/memory/project_linux_graphical.md
+++ b/memory/project_linux_graphical.md
@@ -12,9 +12,10 @@ Linux gameplay or renderer fork.
Canonical seams:
-- `AcDream.Runtime.Platform.ApplicationPathSet` owns XDG/Windows config, data,
- cache, plugin, screenshot, and diagnostic paths for graphical and headless
- hosts.
+- `AcDream.Platform.ApplicationPathSet` (moved out of Runtime by Campaign LA
+ LA0, 2026-08-14) owns XDG/Windows config, data, cache, plugin, screenshot,
+ and diagnostic paths for graphical and headless hosts — and now for the
+ external launcher, which references only `AcDream.Platform`.
- `AcDream.App.Platform.GraphicalHostPlatformServices` owns one startup OS,
architecture, RID, native-dependency manifest, path set, and pacing factory.
- `PlatformFramePacingWaiterFactory` selects the existing Windows
diff --git a/src/AcDream.App/AcDream.App.csproj b/src/AcDream.App/AcDream.App.csproj
index c79ba665..80489ef1 100644
--- a/src/AcDream.App/AcDream.App.csproj
+++ b/src/AcDream.App/AcDream.App.csproj
@@ -58,6 +58,11 @@
+
+
diff --git a/tests/AcDream.Platform.Tests/PlatformDependencyBoundaryTests.cs b/tests/AcDream.Platform.Tests/PlatformDependencyBoundaryTests.cs
new file mode 100644
index 00000000..8b631546
--- /dev/null
+++ b/tests/AcDream.Platform.Tests/PlatformDependencyBoundaryTests.cs
@@ -0,0 +1,62 @@
+using System.Runtime.CompilerServices;
+using System.Xml.Linq;
+
+namespace AcDream.Platform.Tests;
+
+// Campaign LA LA0 review finding 3: AcDream.Platform's entire premise is
+// being the BCL-only assembly the external launcher can reference without
+// pulling any gameplay code. That contract is what this guard enforces —
+// the csproj must declare zero ProjectReference and zero PackageReference
+// entries, forever, in the same spirit as Runtime's and Headless's
+// dependency-boundary guards.
+public sealed class PlatformDependencyBoundaryTests
+{
+ [Fact]
+ public void PlatformProjectDeclaresNoProjectOrPackageDependencies()
+ {
+ var repositoryRoot = FindRepositoryRoot();
+ var projectPath = Path.Combine(
+ repositoryRoot,
+ "src",
+ "AcDream.Platform",
+ "AcDream.Platform.csproj");
+ var project = XDocument.Load(projectPath);
+
+ Assert.Empty(project.Descendants("ProjectReference"));
+ Assert.Empty(project.Descendants("PackageReference"));
+ }
+
+ private static string FindRepositoryRoot(
+ [CallerFilePath] string sourcePath = "")
+ {
+ string[] starts =
+ {
+ Path.GetDirectoryName(sourcePath) ?? string.Empty,
+ Directory.GetCurrentDirectory(),
+ AppContext.BaseDirectory,
+ };
+ foreach (string start in starts)
+ {
+ if (string.IsNullOrEmpty(start))
+ {
+ continue;
+ }
+
+ var directory = new DirectoryInfo(start);
+ while (directory is not null)
+ {
+ if (File.Exists(Path.Combine(
+ directory.FullName,
+ "AcDream.slnx")))
+ {
+ return directory.FullName;
+ }
+
+ directory = directory.Parent;
+ }
+ }
+
+ throw new DirectoryNotFoundException(
+ "Could not find AcDream.slnx above the source, working, or output directory.");
+ }
+}