acdream/docs/plans/2026-08-18-release-stabilization.md
Erik 328b7b456b
Some checks failed
Headless portability / portable-headless (windows-latest) (push) Failing after 42s
Headless portability / portable-launcher (windows-latest) (push) Failing after 4s
Complete Release gate / Complete Release suite (Windows) (push) Failing after 25s
Headless portability / portable-headless (ubuntu-latest) (push) Failing after 13s
Headless portability / portable-launcher (ubuntu-latest) (push) Failing after 1s
Headless portability / linux-graphical (push) Failing after 1s
Headless portability / linux-vulkan (push) Failing after 8s
docs: record stabilization merge handoff
2026-08-18 17:49:09 +02:00

691 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Release stabilization and human-maintainability campaign
**Status:** PROPOSED — plan recorded; implementation not started
**Created:** 2026-08-18
**Audit baseline:** `15539a22a67f8d915d88f8b1d8126cd55eedda6e`
**Evidence:** [`../reviews/2026-08-17-release-maintainability-audit.md`](../reviews/2026-08-17-release-maintainability-audit.md)
**Findings:** [`../reviews/findings-ledger.md`](../reviews/findings-ledger.md)
**Coverage proof:** [`../reviews/coverage-ledger.md`](../reviews/coverage-ledger.md)
## 1. Goal
Prepare acdream for a responsible public release and for maintenance by human
developers who do not have access to prior AI conversations, private worktrees,
or campaign memory.
The campaign succeeds when a new maintainer can clone the repository, identify
the current architecture and supported release, reproduce the build and test
gate, understand why non-obvious retail behavior exists, and publish or roll
back an authenticated release using repository-owned instructions.
This is a stabilization program, not a rewrite. The existing Runtime/App/
Headless architecture remains the foundation unless a slice proves a specific
boundary is wrong.
## 2. Binding principles
1. **Protect behavior before cleanup.** Establish a deterministic complete gate
before broad refactors, comment cleanup, or file decomposition.
2. **Distill knowledge; do not erase it.** No note, comment, issue history,
diagnostic, capture, or raw artifact is removed until its durable value has
a verified destination.
3. **One current truth.** Stable architecture and release state must not depend
on choosing between README, roadmap, milestone, campaign, memory, or
tool-specific instruction copies.
4. **Separate evidence from contracts.** Source comments explain the current
invariant. Dated research records preserve investigation history. Raw
captures live in an explicit artifact tier.
5. **No count-only gates.** A test total is meaningful only when the report says
which hermetic, installed-DAT, live, visual, manual, and diagnostic lanes
actually ran.
6. **Bound every external interaction.** Process waits, network operations,
test runs, and release steps require timeouts, cancellation, and diagnostic
artifacts on failure.
7. **Small reversible slices.** Each slice gets focused tests, a complete gate,
a reviewable commit, a rollback description, and a plan-ledger update.
8. **No opportunistic feature work.** New gameplay features wait unless needed
to prove or repair a release blocker.
## 3. Knowledge-preservation protocol
Every cleanup candidate is classified before it moves:
| Class | Durable value | Destination |
|---|---|---|
| Current invariant | Required behavior, ordering, ownership, threading, or retail rule | Short source comment and/or maintained architecture contract |
| Decision rationale | Alternatives considered, failed attempts, tradeoffs, gate outcome | Dated decision/research record linked from the current contract |
| Reproducible evidence | Minimal fixture, retail symbol/address, script, checksum, expected result | Versioned fixture/research record in Git |
| Raw evidence | Large logs, captures, Ghidra state, screenshots, dumps | Approved versioned artifact store with manifest, hash, provenance, and retention policy |
| Superseded or incorrect claim | Historically useful but no longer operative | Marked `SUPERSEDED` with successor link; archive after references are migrated |
| Duplication/noise | Repeats a preserved fact and adds no independent evidence | Delete only after destination/link validation |
Before deleting or rewriting historical material, all of these must be true:
- its current invariant is recorded at the owning code or architecture seam;
- useful retail provenance, failed approaches, and acceptance evidence remain
searchable under stable identifiers;
- inbound links and source comments point at the surviving destination;
- any raw artifact has an approved distribution, privacy, and licensing status;
- the replacement was reviewed by someone other than its author;
- the complete gate passes after the move.
Git history alone is not the preservation mechanism. History may later be
rewritten to remove large or legally restricted artifacts.
## 4. Campaign dependency map
```text
R0 baseline/authority
-> R1 launcher deadlock
-> R2 reproducible complete gate
-> R3 truthful test lanes
-> R5 documentation authority
-> R6 dead surfaces and tools
-> R7 plugin/config hardening
-> R8 bounded decomposition
-> R10 release candidate
R4 licence/provenance/release governance ---------------------> R10
R9 artifact migration (depends on R4 decisions) --------------> R10
```
R4 starts in parallel because it requires owner/legal decisions. It blocks a
public release but does not block the technical safety work in R1R3.
## 5. Slice map
| Slice | Outcome | Depends on | Release blocking |
|---|---|---|---|
| R0 | Baseline and durable campaign authority accepted | — | yes |
| R1 | Launcher shutdown lock inversion fixed and deterministic | R0 | yes |
| R2 | Pinned clean build and complete bounded CI gate | R1 | yes |
| R3 | Test results truthfully distinguish executed, skipped, and diagnostic work | R2 | yes |
| R4 | Licence, provenance, credential disclosure, and release ownership decided | R0; parallel | yes |
| R5 | One current documentation authority; public docs match the product | R2R3 | yes |
| R6 | Dead presentation/backend/probe surfaces removed; supported tools reproducible | R3R5 | normally yes |
| R7 | Plugin compatibility/lifetime and diagnostic configuration hardened | R3 | yes for advertised plugin release |
| R8 | Highest-risk giant owners decomposed only at proven seams | R3, R6R7 | selective |
| R9 | Large/generated research artifacts moved under an approved policy | R4 | yes if repository is distributed |
| R10 | Clean-clone release candidate and rollback rehearsal | all blocking slices | yes |
## 6. R0 — Baseline and authority
### Work
- Review and accept this plan and the six documents under `docs/reviews/`.
- Record the exact starting commit, SDK, package sources, operating systems,
supported release platforms, and current external prerequisites.
- Decide who owns legal/provenance decisions, CI/release credentials, and final
release approval.
- Pause unrelated feature campaigns until R1R3 establish the safety net.
- Preserve the audit baseline before any cleanup; do not rewrite artifact
history in this slice.
### Exit criteria
- Plan and audit artifacts are tracked in the repository.
- One named owner exists for technical release approval and one for
licensing/provenance approval; the same person may hold both roles.
- The ledger in §17 identifies R1 as the only active implementation slice.
## 7. R1 — Fix the launcher shutdown deadlock first
### Confirmed failure
`LauncherProcessSupervisor.Dispose` currently holds the supervisor `_gate`
while disposing a child. `WindowsSystemChildProcess.Dispose` enters
`System.Diagnostics.Process.Dispose`; concurrently, the process-exit callback
can enter `OnProcessExited` and try to publish state through the same supervisor
gate. The captured wait cycle is:
```text
shutdown: supervisor _gate -> Process internals
exit callback: Process internals -> supervisor _gate
```
The complete solution test process hangs on
`ANullStderrLogPathBehavesExactlyAsBeforeForBothChildProcessKinds`; the
Launcher.Core project can pass alone because the race timing changes.
### Design constraints
- Never call child/process operations that may wait, dispose, raise callbacks,
or execute external code while holding `_gate`.
- Under `_gate`, make only the minimal state transition and snapshot the exact
children/work to retire.
- Perform unsubscribe, stop, kill, wait, and dispose work outside `_gate`.
- Exit callback and explicit disposal must converge idempotently regardless of
which arrives first.
- Preserve exact terminal-state ordering, status publication, graceful-stop
behavior, and child ownership; do not solve the hang by dropping callbacks.
- A failed cleanup must remain observable without starving later cleanup.
### Required tests
- Add a barrier-controlled race test that deterministically pauses the child
exit callback while disposal begins. Do not use `Thread.Sleep` as the oracle.
- Cover exit-before-dispose, dispose-before-exit, simultaneous exit/dispose,
repeated dispose, stop timeout/kill fallback, and callback failure.
- Assert one terminal publication, no resurrection, no orphan child, no held
supervisor lock during process disposal, and bounded completion.
- Run the focused race test repeatedly after its deterministic single pass.
- Run all Launcher.Core tests.
- Run the complete Release solution twice in fresh processes under a documented
timeout and retain hang dumps if either run fails to terminate.
### Exit criteria
- No process/child disposal occurs under the supervisor gate.
- The deterministic race test fails against the baseline mechanism and passes
against the fix.
- Two complete bounded solution runs finish with zero failures.
- F-009 and T-001 receive exact fix commit and gate evidence.
## 8. R2 — Reproducible build and complete CI gate
### Work
- Pin the accepted .NET 10 SDK feature band in `global.json`.
- Centralize common compiler/analyzer settings and package versions; enable
locked restore for release/CI.
- Eliminate all 26 clean-rebuild test warnings or make a narrowly justified,
centrally documented exception fail-safe.
- Build every supported product and every supported tool from a clean checkout.
- Make CI run the complete solution, not only portability subsets. Give each
project/process a timeout and collect test logs plus managed dumps on hangs.
- Preserve focused Windows/Linux portability lanes, but do not represent them
as the complete gate.
- Record restore sources, SDK/runtime, RID, commit, executed/skipped counts, and
artifact hashes in every release report.
### Exit criteria
- A clean clone restores and builds with the pinned toolchain and no warnings.
- Complete CI runs every default release test project and fails on timeout.
- The launcher hang cannot silently consume the CI job indefinitely.
- Package resolution and the gate command are reproducible from repository
instructions alone.
## 9. R3 — Make test reporting truthful
### Required lanes
1. **Hermetic release lane:** default CI; unavailable local data is never a
silent passing return.
2. **Installed-DAT/prepared-package lane:** explicit prerequisites and per-suite
skip identity; result published separately.
3. **Live/connected/visual/listening lane:** operator-owned, dated evidence;
never counted as ordinary unit coverage.
4. **Diagnostic/manual lane:** probes, dumps, fixture generators, and
characterization programs invoked explicitly outside default test totals.
### Work
- Replace the 271 asset/environment empty-return tests with explicit lane
requirements, truthful skips, or hermetic fixtures.
- Move or give stable assertions to the 51 output-only diagnostic methods.
- Delete/replace the three confirmed useless entire cases and the tautological
assertions catalogued in the audit.
- Remove the duplicate theory row and make the clean rebuild warning-free.
- Remove or re-home at least 52 tests with the unreachable panel stack.
- Retire temporary source-shape freezes once a semantic architecture/behavior
guard exists; retain whole-tree dependency guards that express real rules.
- Assign and stabilize the seven documented load-sensitive tests. Do not hide
them with generic retries.
- Inject controllable time into double-click and real-time transport tests.
### Exit criteria
- Default test success means every discovered default contract executed.
- Reports give exact reasons and prerequisite identity for every skip.
- Diagnostics and manual generators do not inflate release regression totals.
- No known duplicate row, literal tautology, or permanent empty scaffold remains
in the default suite.
## 10. R4 — Licence, provenance, security, and release ownership
This slice requires explicit project-owner decisions and, where appropriate,
qualified legal review. The implementation agent records evidence but does not
invent a redistribution basis.
### Work
- Select the project licence and establish contributor/code ownership.
- Audit WorldBuilder-derived code, dependency notices, named-retail/decompiler
exports, PDB-derived data, Ghidra databases, captures, images, and DAT-derived
fixtures for provenance and redistribution status.
- Decide which research artifacts may be public, private, regenerated, or
deleted from distributable history.
- Add SECURITY, CONTRIBUTING, changelog/version authority, disclosure and
deletion behavior for plaintext launcher credentials, and a vulnerability
response path.
- Define release artifact contents, supported platforms, SBOM/provenance,
signing/attestation policy, checksums, update manifest generation, rollback,
and release approval.
### Exit criteria
- Publicly distributed source and artifacts have an approved licence and
complete notices/provenance inventory.
- Users are told exactly how credentials are stored and removed.
- The launcher updater's production manifest and archives are generated and
verified by a repository-owned release process.
## 11. R5 — One current documentation authority
### Work
- Correct public README claims to the actual Vulkan-only client and retained UI.
- Make `docs/README.md` stable navigation plus a generated current-status block
sourced from one structured milestone/release ledger.
- Keep architecture documents limited to durable boundaries, ownership,
threading/lifetime, and data flow. Move commit/test-count/rollback chronology
to dated closeout records.
- Replace duplicated `AGENTS.md`/`CLAUDE.md` product truth with one maintained
tool-neutral source and generated thin adapters; fail CI on drift.
- Mark every memory/plan/spec as current, active, superseded, or historical.
- Normalize active issue/divergence indexes and validate their IDs, statuses,
paths, and links mechanically.
- Apply the knowledge-preservation protocol before shortening any campaign or
issue record.
### Exit criteria
- A new maintainer receives the same current answer from README, documentation
map, architecture, milestone/status ledger, and agent instructions.
- No current authority links missing private `claude-memory`, absent skills, or
developer-local paths.
- Historical records remain searchable but cannot override current truth.
## 12. R6 — Dead surfaces, diagnostics, and tools
### Work
- Decide and then remove or explicitly support the unreachable
`IPanelRenderer`/old panel stack and its tests.
- Remove stale OpenGL/framebuffer/ImGui apparatus and failed temporary-cleanup
markers from shipping assemblies after preserving useful evidence.
- Stop including the smoke plugin in release output by default.
- Classify every tool/script as supported, diagnostic, research-only, or
archived. Repair the five broken C# tools chosen as supported; remove
developer-home/old-worktree paths and document exact prerequisites.
- Centralize environment/diagnostic configuration at composition roots.
### Exit criteria
- Shipping assemblies and package contents contain no abandoned presentation
backend or sample plugin by accident.
- Every supported tool builds from the pinned clean checkout.
- Research-only tools are clearly invoked outside the release build.
## 13. R7 — Plugin and configuration contracts
### Work
- Enforce supported plugin API versions before loading code.
- Implement manifest dependencies or remove the unsupported promise.
- Publish/version `AcDream.Plugin.Abstractions` if plugins are advertised.
- Report registration cleanup and callback failures without preventing
best-effort teardown; prove collectible load-context release under failures.
- Replace absent/null allow-list ambiguity with one explicit production default.
- Replace direct hot-path environment reads/process-static mutable diagnostics
with immutable session-scoped configuration and typed sinks.
### Exit criteria
- An incompatible plugin fails before activation with an actionable message.
- Disable/dispose reports all cleanup failures and cannot silently retain host
registrations.
- Graphical and headless hosts have the same documented plugin/config default.
## 14. R8 — Bounded structural decomposition
This slice begins only after R1R3. File size alone does not authorize a split.
### Priority candidates
- `RuntimeSetPositionState`
- `TransitionTypes`
- `RetailUiRuntime`
- `LiveEntityRuntime`
- `WorldSession`
- `WbDrawDispatcher`
### Rules
- Identify one ownership/lifetime or pure-algorithm seam at a time.
- Preserve a single state owner; do not replace a large class with mirrored
mutable state or a service graph of aliases.
- Prefer partial-file navigation when a state machine must remain one owner.
- Establish behavior/sabotage tests before extraction and remove corresponding
temporary source-text freezes afterward.
- Replace Chorizite/GL vocabulary in prepared-content DTOs with versioned
acdream-owned semantics at a separately reviewed boundary.
### Exit criteria
- Each extraction reduces change coupling or improves ownership clarity; line
count reduction alone is not success.
- Runtime behavior, retail evidence, allocations, and teardown ledgers remain
equivalent under the relevant focused and complete gates.
## 15. R9 — Repository artifact migration
### Work
- Inventory the approximately 575 MiB of Ghidra state, 299 MiB research tree,
and 151 MiB tracked logs by provenance, sensitivity, reproducibility, and
ongoing value.
- Keep compact fixtures, scripts, tool versions, summaries, and checksums in
Git. Move approved raw bundles to a versioned artifact store.
- Add a manifest/bootstrap command that verifies artifact identity.
- Define retention, redaction, access, and backup policy.
- Treat Git-history rewriting as a separately approved migration with backup,
contributor coordination, remote replacement, and verification. Never do it
as an incidental cleanup command.
### Exit criteria
- A normal clone contains what build/test/maintenance requires without opaque
generated databases or raw logs.
- Authorized researchers can retrieve exact approved evidence by manifest and
hash.
- Restricted or non-redistributable material is absent from public history.
## 16. R10 — Release-candidate gate
From a fresh clone on every supported release platform:
- restore with the pinned, locked toolchain;
- build every shipped product and supported tool with zero warnings;
- run the complete hermetic test lane with no failures, silent no-ops, hangs,
or generic retries;
- run and report the applicable DAT, connected, visual/listening, updater,
installer, graceful-shutdown, and rollback gates;
- generate versioned per-RID packages, plugin abstraction package if supported,
SBOM/provenance/checksums, and updater manifest;
- install/update/rollback using only public release instructions;
- verify package contents contain no credentials, developer paths, smoke
plugin, raw probes, or unapproved research artifacts;
- obtain technical and licensing/provenance approval.
Public release remains **NO-GO** until every blocking slice is closed.
## 17. Cross-session execution ledger
This table is the resume authority. Update it in the same commit as every slice
checkpoint. Do not infer status from chat history.
| Slice | Status | Commit(s) | Evidence/gates | Exact next action |
|---|---|---|---|---|
| R0 | PLAN ACCEPTED; R4 OWNER DECISION DEFERRED | — | Audit complete at `15539a22`; user completed R1R3 | Retain the plan and audit artifacts as campaign authority |
| R1 | COMPLETE; MERGE AUTHORIZED 2026-08-18 | `0a934cf5` | 2026-08-18 checkpoint below; F-009/T-001 resolved and committed | Merge with the complete R1R3 stabilization branch |
| R2 | COMPLETE; MERGE AUTHORIZED 2026-08-18 | `2ac05486`, `c38f6b88` | Checkpoints below; F-014/F-019 resolved, F-010 machine-readable, supported .NET tool portion of F-004 resolved | Merge with the complete R1R3 stabilization branch |
| R3 | COMPLETE; MERGE AUTHORIZED 2026-08-18 | `14d371a0`, `b64c8041` | Final inventory: 1,254 files, 11,414 attributed methods, 22 approved source readers; Release gate: 14,346/14,346 | Merge the stabilization branch; retain the closeout ledger as authority |
| R4 | DEFERRED BY USER FOR FRIEND-ONLY RELEASE | — | F-001/F-026/F-031/F-034 remain public-release blockers | Resume before any public release; keep credentials and research artifacts out of friend packages |
| R5 | DEFERRED BY USER | — | F-002/F-003/F-006/F-007/F-011/F-020/F-027/F-029/F-032 | Resume later with the bounded documentation-authority goal |
| R6 | NOT STARTED | — | F-004/F-005/F-012/F-016/F-023/F-028 | Wait for R3/R5 |
| R7 | NOT STARTED | — | F-017/F-018/F-024/F-025 | Wait for R3 |
| R8 | NOT STARTED | — | F-008/F-013/F-033 | Wait for R1R3 and cleanup decisions |
| R9 | NOT STARTED | — | F-034 plus R4 provenance decisions | Wait for R4 |
| R10 | NOT STARTED | — | All blocking findings | Wait for blocking slices |
### R1 implementation checkpoint — 2026-08-18
**Working-tree base:** `15539a22a67f8d915d88f8b1d8126cd55eedda6e`
**Commit:** `0a934cf5` on `codex/release-stabilization`; the checkpoint is
durable on that campaign branch but is not yet merged to `main`.
Implementation:
- `LauncherProcessSupervisor.Dispose` now transfers `_process` ownership to a
local and clears the field under `_gate`, then performs stop, event removal,
and child disposal outside the gate.
- Public `Stop` and disposal share one `StopProcess` implementation, preserving
graceful-stop, close-window, timeout, kill, and post-kill observation order.
- `OnProcessExited` reads the event sender's optional exit code without holding
`_gate`, then commits the terminal transition through the existing ordered
state publisher. A callback already captured during teardown may therefore
finish instead of forming the supervisor/Process lock cycle.
- `DisposeAllowsAnAlreadyCapturedExitCallbackToComplete` uses explicit barriers:
the fake captures the exit delegate before unsubscription; its disposal
releases the callback and waits for it to return. There are no timing sleeps
in the oracle, and an emergency release keeps failure against the old code
bounded rather than wedging the test host.
Sabotage and focused evidence:
- With the old lock shape temporarily restored and the new test retained, the
test failed with its expected five-second timeout. The fake's cleanup barrier
then released the old cycle, so the test process exited normally.
- With the fix restored, the same test passed in 21 ms.
- The race test passed 25/25 times in fresh `dotnet test` processes.
- All `LauncherProcessSupervisorTests` passed: 22/22.
- Complete Launcher.Core passed under a 180-second hard process bound:
339 passed / 0 skipped / 0 failed in 52 seconds.
Complete-solution evidence:
- Release build completed inside a 300-second bound with 0 warnings / 0 errors
in the evaluated incremental build.
- The exact serialized command was
`dotnet test AcDream.slnx -c Release --no-build --no-restore --nologo -m:1`,
launched in a fresh child process for each run. An outer process watchdog
allowed 900 seconds, killed the complete process tree on expiry, and treated
timeout as failure.
- Run 1: 12 assemblies, 14,748 passed / 77 skipped / 0 failed, 1:28.822,
bounded exit code 0.
- Run 2: 12 assemblies, 14,748 passed / 77 skipped / 0 failed, 1:30.241,
bounded exit code 0.
Adjacent evidence, deliberately not folded into R1:
- One default-parallel whole-solution run terminated normally in 58.148
seconds—important negative evidence for the former hang—but failed one
`AcDream.Launcher.Tests` Avalonia headless cleanup because a compositor was
accessed from a non-owning thread. The Launcher test project then passed
67/67 alone. R1 makes no Avalonia changes; R2/R3 must decide the supported CI
scheduling and ownership of that pre-existing parallel-run failure.
- The known duplicate Core theory row and 77 skip classifications remain
unchanged and belong to R3. R1 does not use their headline count as proof of
test quality.
Changed implementation/test files:
- `src/AcDream.Launcher.Core/Launching/LauncherProcessSupervisor.cs`
- `tests/AcDream.Launcher.Core.Tests/Launching/LauncherProcessSupervisorTests.cs`
Rollback is `git revert 0a934cf5`; do not rewrite branch history.
### R2 complete-gate checkpoint — 2026-08-18
**Working-tree base:** `0a934cf5781c003375c14af9a1f565254df0f9f9`
**Commit:** `2ac05486` on `codex/release-stabilization`; the checkpoint is
durable on that campaign branch but is not yet merged to `main`.
Implemented gate:
- `global.json` pins the accepted .NET 10 SDK feature band at `10.0.300` with
`latestPatch` roll-forward and prerelease SDKs disabled. Every existing
`actions/setup-dotnet` step now reads that file instead of floating on
`10.0.x`.
- `tools/run-release-gate.ps1` discovers every project under `tests/` that
declares `Microsoft.NET.Test.Sdk` or `IsTestProject`, verifies the project is
present in `AcDream.slnx`, restores/builds the solution, and runs each test
assembly exactly once in its own Release process. It does not retry.
- Restore, build, and each test process have 600/900/600-second outer bounds.
Tests additionally use VSTest's 180-second per-test blame-hang collector with
mini dumps. An outer timeout kills the complete process tree and reports exit
code 124; GitHub Actions adds a 45-minute job bound.
- Every run writes exact commands and output, one TRX per assembly, any blame
sequence/dumps, `dotnet --info`, configured NuGet sources, commit/branch/RID,
aggregate executed/passed/skipped/failed counts, and `SHA256SUMS.txt`.
- `.github/workflows/release-gate.yml` runs the gate on pull requests, pushes
to `main`, and manual dispatch on `windows-latest`, then uploads the evidence
even when the gate fails. The focused Windows/Linux portability and Vulkan
lanes remain separate and are no longer the only deterministic CI coverage.
- `docs/release-gate.md` is the repository-owned local/CI runbook.
Test-isolation corrections, with no product behavior change:
- Four `MainWindowViewTests` that call `Show()` now close their window and pump
dispatcher cleanup in `finally` on the owning Avalonia test session. The old
tests leaked shown, thread-affine compositor state to runner teardown; no
suite serialization or retry was added.
- The complete gate's first evidence run correctly failed
`RealChildStderrIsCapturedForTheProcessStartInfoPath`: its live polling helper
briefly denied write sharing, so the final async stderr callback observed an
`IOException` and the deliberately no-throw capture sink latched off. The
helper now reads with `FileShare.ReadWrite | FileShare.Delete`, matching the
production status tailer; its assertions and five-second bound are unchanged.
Verification:
- Focused `MainWindowViewTests`: 13 passed / 0 skipped / 0 failed.
- Three fresh default-parallel whole-solution runs completed inside independent
180-second process bounds with 12/12 TRX files and no Avalonia cleanup error:
56.719, 56.321, and 58.506 seconds. Each reported 14,748 passed / 77 skipped /
0 failed.
- The stderr ProcessStartInfo test passed 25/25 fresh-process repetitions after
the live-reader correction.
- The actual outer-watchdog function killed a controlled fixture process tree
at 2.107 seconds, returned 124, and left no child process.
- The repository command `pwsh ./tools/run-release-gate.ps1` completed in
107.337 seconds on SDK `10.0.300`, RID `win-x64`: 12 assemblies, 14,748
executed and passed / 77 skipped / 0 failed. The evidence manifest contains
28 verified SHA-256 entries.
Deliberately still open in the wider R2 slice:
- the 26 warnings observed by a clean recompilation, centralized compiler and
package settings, package lock files/locked restore, and broken or unsupported
tool-project decisions;
- the known duplicate Core theory row and classification of the 77 skips, which
remain R3 work; and
- stale public headline counts, which must be corrected with the documentation
authority work rather than hand-edited as part of this gate checkpoint.
Therefore this checkpoint closes F-014 on the campaign branch and the SDK part
of F-019, and gives F-010 a truthful machine-readable count. It does not claim
the broader R2 reproducibility slice or R3 test-quality cleanup is complete.
Rollback is `git revert 2ac05486`; do not rewrite branch history.
### R2 reproducibility closeout — 2026-08-18
**Working-tree base:** `b459e0cf0cab9241b0771d2ce6838083f2c84162`
**Implementation commit:** `c38f6b88522750abc2e4acf1898ed566c5576e4a`
on `codex/release-stabilization`; the closeout is durable on that campaign
branch but is not yet merged to `main`.
Repository policy and dependency graph:
- `Directory.Build.props` is the common .NET 10, language, nullable, latest
analysis, warnings-as-errors, deterministic-build, and lock-file authority.
- `Directory.Packages.props` centrally pins all 30 direct package versions.
All 89 `PackageReference` sites are versionless; no project-local version can
silently drift.
- `NuGet.Config` clears machine fallback folders and package sources, then
declares only `nuget.org`.
- Every supported project owns `packages.neutral.lock.json` (44 files). Every
shippable source project additionally owns `packages.win-x64.lock.json` and
`packages.linux-x64.lock.json` (14 of each; 72 graphs total). Conventional
`packages.lock.json` files are intentionally absent because NuGet gives that
filename precedence over `NuGetLockFilePath`, preventing adjacent neutral
and RID graphs.
- `tools/update-package-locks.ps1` is the one intentional update path. The
release gate and the launcher's nested Bake publish use forced locked restore,
so stale `obj/` assets cannot hide a disagreement and a normal gate cannot
rewrite dependency resolution.
Complete maintained build surface:
- All 13 tracked .NET tools were repaired against package/owned interfaces,
documented in `tools/README.md`, and added to `AcDream.slnx`.
- The gate now verifies that every `.csproj` under `src/`, `tests/`, and
`tools/` is a solution member, requires the expected lock graphs, and records
their hashes in the evidence bundle. The supported graph is 44 projects.
- The older script/probe archive under F-004 is unchanged. Classifying that
historical material remains R6 work; the R2 change only makes the maintained
.NET tools truthful and reproducible.
Warning cleanup and test-gate stability:
- A clean complete recompilation originally exposed 26 warnings, all in test
and diagnostic code. Assertion-specific analyzers, nullable test doubles,
and nullable DAT probe boundaries were corrected without changing product
behavior.
- The one known redundant historical Core theory input remains for R3 behind a
site-scoped `xUnit1025` suppression. The central policy still makes any new
duplicate row a build failure.
- The launcher's seven editor-focus variants now execute in one Avalonia test
application session. This prevents the headless framework from attempting
compositor reinitialization on a non-owning thread. The suite passed 11
consecutive focused runs before the full gate. Aggregating seven theory rows
into one fact reduces the headline passed count by six; all seven variants
are still executed and asserted.
- The launcher package-boundary test now verifies versionless project
references against the central version table rather than incorrectly
requiring inline versions.
Reproducibility evidence:
- Forced locked re-evaluation of all 44 neutral and all 28 RID graphs changed
zero lock hashes.
- A locked restore into an empty global package cache, with `--no-cache`,
succeeded from the sole configured source. NuGet assets recorded no fallback
package folder.
- A forced nested launcher-to-Bake publish selected the appropriate RID graph,
emitted the Bake executable, and changed zero lock hashes.
- `pwsh ./tools/run-release-gate.ps1` ran on the clean exact commit
`c38f6b88522750abc2e4acf1898ed566c5576e4a`, SDK `10.0.300`, RID `win-x64`,
in 121.398 seconds. Restore was forced and locked; all 44 projects built with
0 warnings / 0 errors; all 12 test assemblies completed with 14,742 passed /
77 skipped / 0 failed. The gate recorded `WorktreeDirty: false`.
No product source behavior changed in this closeout. The known duplicate theory
row, classification of the 77 environment-dependent skips, test naming/value
review, and stale public headline counts remain explicitly assigned to R3 and
the later documentation-authority slice. R2 is complete on the campaign branch.
Rollback is `git revert c38f6b88`; do not rewrite branch history.
## 18. Session start protocol
Every implementation session begins by:
1. reading this plan, the executive audit, and the findings for the active
slice;
2. running `git status --short`, `git rev-parse HEAD`, and checking the ledger's
recorded commit against the working tree;
3. reading all files/tests named by the active finding before editing;
4. confirming there is no overlapping uncommitted user work;
5. restating the bounded slice outcome and gates in the session update;
6. working only the first non-blocked active slice unless the plan explicitly
allows parallel work.
## 19. Session handoff protocol
Before ending any session, record in §17 or a linked dated closeout:
- exact commit/worktree state and every file changed;
- decisions made and alternatives rejected;
- invariant/evidence destinations for anything removed;
- exact commands, pass/fail/skip counts, timeouts, and artifact paths;
- review findings and whether they were closed;
- remaining risks, blockers, and user/legal decisions;
- rollback command or precise reversal procedure;
- one exact next action that can be started without chat context.
A slice is not `DONE` because its code compiles or a focused test passes. It is
done only when its exit criteria, complete required gate, evidence update,
review, and cross-session ledger entry are all complete.
## 20. Immediate next action
Fast-forward `main` through the completed R1R3 stabilization branch and push
the merged result. R4 and R5 are explicitly deferred for the friend-only
release. Before any public release, resume R4; when maintainability work
resumes, begin with the bounded R5 documentation-authority goal. Do not begin
bulk comment, artifact, giant-file, or unrelated cleanup first.