From a49e92df3aacc6c059b6042fab7dee8d381285de Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 14 Aug 2026 15:30:04 +0200 Subject: [PATCH] =?UTF-8?q?fix(platform):=20Campaign=20LA=20LA0=20review?= =?UTF-8?q?=20fixes=20=E2=80=94=20CI=20Linux=20lanes,=20arch=20doc,=20self?= =?UTF-8?q?-guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Opus dual-lens review of cb6502c8 passed with six findings; this lands the fix round: 1. headless-portability.yml: AcDream.Platform src/tests join both path triggers and the presentation-free build/test arrays — the moved XDG tests run on ubuntu-latest again (they had fallen out of every Linux lane). 2. acdream-architecture.md: AcDream.Platform gets its own layer block; Runtime may-reference clause updated (the guard changed in cb6502c8, its human-readable twin had not). 3. PlatformDependencyBoundaryTests: the BCL-only contract (zero project/package references) is now enforced, not just observed. 4. memory/project_linux_graphical.md canonical seam renamed. 5. Plan LA0 recon corrected: the K0 Headless guard was never the guard needing amendment (it asserts Headless own refs); Runtime own-refs guard was — the commit did the right thing, the plan text now says so. 6. App declares its AcDream.Platform reference explicitly per its own convention instead of riding transitivity. Platform.Tests: 4 passed (3 moved + the new guard). Co-Authored-By: Claude Fable 5 --- .github/workflows/headless-portability.yml | 6 ++ docs/architecture/acdream-architecture.md | 15 +++-- docs/plans/2026-08-14-launcher-campaign.md | 22 ++++--- memory/project_linux_graphical.md | 7 ++- src/AcDream.App/AcDream.App.csproj | 5 ++ .../PlatformDependencyBoundaryTests.cs | 62 +++++++++++++++++++ 6 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 tests/AcDream.Platform.Tests/PlatformDependencyBoundaryTests.cs 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."); + } +}