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 <noreply@anthropic.com>
Closes out the GL deletion by fixing the CI workflow and developer gate
scripts that still assumed a GL arm existed to compare against, build,
or select via ACDREAM_RENDER_BACKEND.
.github/workflows/headless-portability.yml: linux-graphical's "Verify
actionable unsupported-driver gate" step is deleted outright — it ran
the deleted `ui-studio` CLI verb (Studio was removed at Commit 1) to
prove the GL capability gate rejects Mesa's llvmpipe driver, and there
is no more GL capability gate for any driver to pass or fail. Its test
filter dropped two dead entries (GraphicalCapabilityRequirementsTests,
deleted at Commit 2; StudioWindowTests, already gone). Its package
contract check dropped the libcimgui.so assertion (ImGui's native
bridge, deleted at Commit 1). linux-vulkan's explanatory comment, which
described GL's rejection as the reason no cross-backend pixel diff runs
in CI, is rewritten to explain there is no GL arm left at all. Two dead
src/AcDream.UI.ImGui/** path triggers (that project no longer exists)
are removed from both the pull_request and push filters.
tools/run-backend-differential-gate.ps1 and its dedicated route file
tools/connected-backend-differential.route.txt are deleted: the whole
script's purpose was comparing a GL launch against a Vulkan launch of
the same route, and there is no second arm left to compare. Single-arm
regression checking already exists via run-offline-pixel-gate.ps1's
-Baseline mechanism.
tools/run-portal-churn-soak.ps1 is simplified rather than deleted: its
repeated-portal-churn methodology (within-arm capture comparison,
memory/entity/GPU trend analysis) has value independent of the
GL-versus-Vulkan question it was built to answer for issues #256/#257
before V11. -Backends now defaults to @('vulkan') alone; the doc
comments are rewritten from "step 0 discriminator, run before V11" to
an ongoing single-arm regression soak.
tools/run-offline-pixel-gate.ps1 drops its now-nonfunctional -Backend
parameter (ACDREAM_RENDER_BACKEND has read zero call sites since
RuntimeOptions.RenderBackend was removed at Commit 2 — passing -Backend
gl silently launched Vulkan anyway) along with its GL-escape-hatch
example and every comment that referenced the now-deleted differential
gate. tools/run-connected-world-lifecycle-gate.ps1,
tools/run-offline-vulkan-capture.ps1, and
tools/run-repeat-connected-gate.ps1 keep their (harmless, already
no-op) ACDREAM_RENDER_BACKEND set/clear lines but have their
now-inaccurate "escape hatch" / "GL run" comments corrected to state
plainly that the variable is unread and the line is kept only for the
historical record.
No .cs files touched; `dotnet build AcDream.slnx -c Release` unaffected
(0 warnings, 0 errors, matching the prior commit's build).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The lavapipe job did the thing it was built to do on its first attempt.
It accepted a Cpu device at API 1.4, created a device and read pixels
back, captured a real frame, and exited 4 when a feature was forced
unsupported. Three other things were red, and none of them were the
Vulkan backend.
The shader-freshness step aborted for two separate Linux faults in the
compiler tool. Disposing the Silk.NET API container unloads the native
module, and dlclose-ing libshaderc_shared.so leaves glslang's
process-level teardown running against unmapped code. Bisected with a
four-mode probe on Ubuntu 24.04: GetApi, CompilerInitialize and
CompilerRelease each exit 0, and adding only the container Dispose turns
the exit into SIGSEGV. That is the 134 CI reported. shaderc's own handles
are still released; the container is not, because the module's lifetime
is the process's and the process is one statement from returning.
Separately, a portable dotnet build leaves the native under
runtimes/linux-x64/native/ and makes reaching it Silk.NET's probing
problem, which it solved on a local Ubuntu 24.04 and did not solve on the
runner. The script now publishes the tool for the host RID, so the native
sits beside the assembly where AppContext.BaseDirectory finds it, and
checks for it by name so a regression says which file is missing rather
than which names failed.
With both fixed, the question section 5.5.20 left open has an answer:
Linux shaderc and Windows shaderc agree byte-for-byte at the pinned Silk
2.23.0. Eighteen of eighteen .spv identical, manifest identical. The byte
comparison stays a byte comparison.
The Windows leg of portable-headless was running sudo apt-get. That step
is older than this campaign - it is red in the 2026-07-27 main run too -
and it was misplaced rather than mis-conditioned. Nothing in that job
opens a display or links GL, and the graphical jobs that do call xvfb-run
take it from the runner image, so the step is deleted rather than
guarded. Every remaining step in the two-operating-system matrix is pwsh;
every bash step now lives in an ubuntu-only job.
The last failure was ours in a quieter way. WaitForCharacterLogOff-
Confirmation expressed its deadline only as a CancellationTokenSource,
whose timeout is published from a thread-pool timer callback, so on a
saturated pool the token stays unsignalled past the deadline while the
loop keeps draining items that are already queued. That is the case the
method exists to bound. Reproduced by pinning the suite to two CPUs on
Linux, which failed 2 of 6 where four CPUs and sixteen were clean, and
where CI failed 3 of 3. The drain now reads the deadline off the
monotonic clock as well; the token still bounds the asynchronous wait.
Ten of ten clean under the same pin. The test is untouched. Filed as
Release build green. App tests 4,152 / 3 skipped against the same 4,152 /
3 measured at base 32f9bcfa. Core.Net 600 / 600.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The first CI job in this project's history that renders a frame.
The whole row rests on a decision V6g already made and paid for. When
section 5.5.8 cut set 0 from ten dynamic storage descriptors to four, four
was not merely under the RX 9070 XT's eight - it is Vulkan's guaranteed
minimum, so no conformant device can fail the layout. That is what makes a
software-device row possible at all. Every other requirement was then
checked against Mesa's lvp_device.c rather than assumed, and all seventeen
features the gate demands are true on lavapipe - including
samplerAnisotropy, which V7 made load-bearing eight commits ago and which a
software rasterizer would have been entirely within its rights to decline.
Three things had to exist before the job could:
1. The harness could not stop. VulkanBringUpHost presents until its window
closes, which is right at a desk and impossible in CI, where nothing ever
closes a window. ACDREAM_VULKAN_PROBE_FRAMES gives it a budget; unset or
malformed is zero, which keeps the interactive behaviour, so no existing
invocation changes. The budget never cuts the capture short - the loop
stays open until the screenshot has been attempted - because a run whose
entire product is a PNG must not be able to exit green with an empty
artifact directory. The decision is a pure static method, tested without
a window or a driver.
2. tools/compile-shaders.ps1 was Windows-only and nobody had noticed,
because nothing had ever run it anywhere else. It built its paths from
embedded 'src\AcDream.App\...' literals; a backslash is a separator on
Windows and an ordinary filename character everywhere else, so on Linux
that is one long nonexistent file name.
3. The report's jq paths were invisible to the compiler. Renaming a record
property or swapping the enum converter would have left every test green
and turned CI red on someone else's branch days later, with a failure
that reads like a driver problem. VulkanCapabilityReportContractTests
pins the exact strings the job greps and pins its packed-version
arithmetic against VulkanApiVersion's own unpacking.
The job, eleven steps: install lavapipe and Xvfb; record vulkaninfo as
evidence; publish linux-x64; run the Gpu.Vk tests on a second operating
system; probe the gate under a 24-bit Xvfb screen (the default is 8-bit,
which leaves the X11 WSI without a usable visual) and assert an accepting
verdict on a Cpu device at API >= 1.3 with a clean active probe; assert the
captured PNG is a real frame by IHDR dimensions and byte count; re-run with
ACDREAM_VULKAN_FORCE_UNSUPPORTED=timelineSemaphore and assert exit 4 with an
actionable refusal; recompile the shaders and compare. Artifacts upload on
always(), so a red run ships its own diagnosis.
The .spv step is what ties the committed binaries to their sources. The
existing App test hashes GLSL against the manifest, which catches "edited a
shader, forgot to recompile"; nothing caught a stale or hand-edited .spv.
Verified on Windows before shipping: 19/19 artifacts byte-identical to a
fresh compile, zero drift.
No GL-versus-Vulkan pixel compare, for two independent reasons recorded in
section 5.5.20: linux-graphical asserts exit 4, so there is no left-hand
side, and the probe renders synthetic scenes rather than the DAT world CI
cannot have. The two jobs now say something sharper than a pixel diff would
have - on the same software Mesa stack, GL is refused and Vulkan is accepted
and draws. Physical Linux GPU and Wayland rows stay deferred on the Slice L
precedent; no hosted runner offers either.
Gates: Release build green, zero errors. App tests 4,152 / 3 skipped against
a 4,134 / 3 baseline at this branch's base (9b7f4343) - eighteen new, all
from this slice. Workflow validated by a real YAML parse plus an Actions
schema check and bash -n over all nine extracted run blocks; no actionlint
was available locally and none was downloaded. The job itself has not run:
its first execution is the CI run this commit triggers, and the V9 row stays
partial until that is green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add the presentation-free acdream-headless executable, strict no-connect configuration validation, dependency and assembly guards, and a Windows/Ubuntu CI lane that builds and tests only the portable runtime closure.
Co-authored-by: Codex <noreply@openai.com>