diff --git a/packages/org.gamecult.eve.surface/GameCult.Eve.Surface.csproj b/packages/org.gamecult.eve.surface/GameCult.Eve.Surface.csproj
index 442ae1a..72b5b3f 100644
--- a/packages/org.gamecult.eve.surface/GameCult.Eve.Surface.csproj
+++ b/packages/org.gamecult.eve.surface/GameCult.Eve.Surface.csproj
@@ -5,6 +5,14 @@
latest
enable
Portable Eve surface and provider contracts.
+ GameCult.Eve.Surface
+ 0.3.2
+ GameCult
+ MPL-2.0
+ https://github.com/GameCult/Eve
+ https://github.com/GameCult/Eve
+ README.md
+ eve;cultui;cultmesh;multiplayer;ui
$(MSBuildThisFileDirectory)..\..\..\CultLib
@@ -12,6 +20,9 @@
+
+
+
+
-
diff --git a/packages/org.gamecult.eve.surface/README.md b/packages/org.gamecult.eve.surface/README.md
index bd31b4d..b0e6dd5 100644
--- a/packages/org.gamecult.eve.surface/README.md
+++ b/packages/org.gamecult.eve.surface/README.md
@@ -16,15 +16,19 @@ bindings through `StateBindings`, and nested synced document regions through
schema, presentation kind, and route hint; renderers resolve that child through
CultMesh instead of rebuilding it with runtime-local projection code.
-See `../../docs/surface-contract-v1.md` for the full contract and
-`../../web/fixtures/cultui-embedded-surface.json` for the parity fixture.
-Runtime discovery and required feature coverage live in
-`../../tools/parity/parity-manifest.json`; active GUI runtimes must list
+See the
+[surface contract](https://github.com/GameCult/Eve/blob/main/docs/surface-contract-v1.md)
+for the full contract and the
+[embedded-surface fixture](https://github.com/GameCult/Eve/blob/main/web/fixtures/cultui-embedded-surface.json)
+for parity evidence. Runtime discovery and required feature coverage live in
+the [parity manifest](https://github.com/GameCult/Eve/blob/main/tools/parity/parity-manifest.json);
+active GUI runtimes must list
`embeddedDocuments` as a supported feature and require the `embedded-surface`
fixture.
Verification entrypoints:
+- clean .NET package consumer: `powershell -ExecutionPolicy Bypass -File .\scripts\pack-dotnet-surface.ps1`
- C# serialization: `dotnet test tests/GameCult.Eve.Surface.Serialization.Tests/GameCult.Eve.Surface.Serialization.Tests.csproj`
- Browser/TypeScript: `node --test web\eve-dsl.test.mjs`
- Semantic runtime matrix: `powershell -ExecutionPolicy Bypass -File .\scripts\run-parity-harness.ps1`
diff --git a/scripts/pack-dotnet-surface.ps1 b/scripts/pack-dotnet-surface.ps1
new file mode 100644
index 0000000..963c843
--- /dev/null
+++ b/scripts/pack-dotnet-surface.ps1
@@ -0,0 +1,99 @@
+param(
+ [string] $CultLibRoot = "",
+ [string] $Configuration = "Release",
+ [string] $OutputDirectory = "artifacts\nuget",
+ [switch] $SkipConsumerSmoke
+)
+
+$ErrorActionPreference = "Stop"
+$repoRoot = Split-Path -Parent $PSScriptRoot
+if ([string]::IsNullOrWhiteSpace($CultLibRoot)) {
+ $CultLibRoot = Join-Path (Split-Path -Parent $repoRoot) "CultLib"
+}
+$cultLibPath = [IO.Path]::GetFullPath($CultLibRoot)
+$cultLibPack = Join-Path $cultLibPath "scripts\pack-nuget.ps1"
+if (-not (Test-Path -LiteralPath $cultLibPack)) {
+ throw "CultLib NuGet packer was not found at '$cultLibPack'."
+}
+$outputRoot = if ([IO.Path]::IsPathRooted($OutputDirectory)) {
+ [IO.Path]::GetFullPath($OutputDirectory)
+} else {
+ [IO.Path]::GetFullPath((Join-Path $repoRoot $OutputDirectory))
+}
+
+& $cultLibPack -Configuration $Configuration -OutputDirectory $outputRoot -SkipConsumerSmoke
+if ($LASTEXITCODE -ne 0) {
+ throw "CultLib NuGet dependency pack failed with exit code $LASTEXITCODE."
+}
+
+$surfaceProject = Join-Path $repoRoot "packages\org.gamecult.eve.surface\GameCult.Eve.Surface.csproj"
+dotnet pack $surfaceProject -c $Configuration -o $outputRoot `
+ -p:CultLibRoot=$cultLibPath -p:NoWarn=1591%3BCS8632 `
+ --nologo --verbosity quiet
+if ($LASTEXITCODE -ne 0) {
+ throw "Eve surface NuGet pack failed with exit code $LASTEXITCODE."
+}
+
+$surfacePackage = Get-ChildItem -LiteralPath $outputRoot -Filter "GameCult.Eve.Surface.*.nupkg" |
+ Where-Object { $_.Name -notlike "*.snupkg" } |
+ Sort-Object LastWriteTimeUtc -Descending |
+ Select-Object -First 1
+if (-not $surfacePackage) {
+ throw "GameCult.Eve.Surface package was not produced."
+}
+$version = [regex]::Match(
+ $surfacePackage.Name,
+ '^GameCult\.Eve\.Surface\.(.+)\.nupkg$').Groups[1].Value
+
+if (-not $SkipConsumerSmoke) {
+ $smokeRoot = Join-Path ([IO.Path]::GetTempPath()) ("eve-surface-consumer-" + [guid]::NewGuid().ToString("N"))
+ New-Item -ItemType Directory -Force -Path $smokeRoot | Out-Null
+ try {
+ $projectDocument = @"
+
+
+ net10.0
+ Exe
+ enable
+ $outputRoot;https://api.nuget.org/v3/index.json
+
+
+
+
+
+"@
+ $program = @"
+using GameCult.Eve.Surface;
+
+var surface = EveSurface.Create("sample.counter")
+ .Provider("sample.counter-provider", "sample.daemon")
+ .Title("Clean package consumer")
+ .RootColumn("root", root => root.Title("title", "Portable Eve"))
+ .Build();
+if (surface.Schema != EveSurfaceDocument.SchemaId)
+ throw new InvalidOperationException("The packed surface contract returned the wrong schema.");
+Console.WriteLine(surface.Schema);
+"@
+ [IO.File]::WriteAllText(
+ (Join-Path $smokeRoot "Consumer.csproj"),
+ $projectDocument,
+ [Text.UTF8Encoding]::new($false))
+ [IO.File]::WriteAllText(
+ (Join-Path $smokeRoot "Program.cs"),
+ $program,
+ [Text.UTF8Encoding]::new($false))
+ dotnet run --project (Join-Path $smokeRoot "Consumer.csproj") -c $Configuration `
+ --nologo --verbosity quiet
+ if ($LASTEXITCODE -ne 0) {
+ throw "Eve surface clean NuGet consumer failed with exit code $LASTEXITCODE."
+ }
+ } finally {
+ if (Test-Path -LiteralPath $smokeRoot) {
+ Remove-Item -LiteralPath $smokeRoot -Recurse -Force
+ }
+ }
+}
+
+Write-Host "Eve .NET package feed: $outputRoot"
+Write-Host "GameCult.Eve.Surface: $version"
+Write-Host "Clean PackageReference consumer: $(-not $SkipConsumerSmoke)"