fix: prevent launcher exit disposal deadlock
This commit is contained in:
parent
15539a22a6
commit
0a934cf578
9 changed files with 2596 additions and 15 deletions
538
docs/plans/2026-08-18-release-stabilization.md
Normal file
538
docs/plans/2026-08-18-release-stabilization.md
Normal file
|
|
@ -0,0 +1,538 @@
|
|||
# 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 R1–R3.
|
||||
|
||||
## 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 | R2–R3 | yes |
|
||||
| R6 | Dead presentation/backend/probe surfaces removed; supported tools reproducible | R3–R5 | 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, R6–R7 | 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 R1–R3 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 R1–R3. 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; tracking/owner assignment pending | — | Audit complete at `15539a22`; user started R1 | Track plan+audit artifacts; assign R4 owner |
|
||||
| R1 | IMPLEMENTED + REQUIRED GATES PASS; review/commit pending | — | 2026-08-18 checkpoint below; F-009/T-001 resolved in working tree | Review diff, then commit the coherent R1 checkpoint |
|
||||
| R2 | NOT STARTED; technically unblocked after R1 review/commit | — | F-010/F-014/F-019 | Start only after R1 checkpoint is accepted |
|
||||
| R3 | NOT STARTED | — | T-002–T-016; F-015/F-021/F-022/F-030 | Wait for R2 |
|
||||
| R4 | NOT STARTED; may run parallel | — | F-001/F-026/F-031/F-034 | Assign owner/legal provenance decision path |
|
||||
| R5 | NOT STARTED | — | F-002/F-003/F-006/F-007/F-011/F-020/F-027/F-029/F-032 | Wait for R2–R3 |
|
||||
| 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 R1–R3 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:** pending; do not describe this checkpoint as merged or durable until
|
||||
the source, test, plan, and audit records are committed together.
|
||||
|
||||
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 before commit is the exact reverse of those two file diffs. After a
|
||||
future commit, record its hash here and use `git revert <R1-commit>` rather than
|
||||
rewriting 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
|
||||
|
||||
Review the R1 diff and checkpoint evidence, then commit the source, regression
|
||||
test, plan, and audit updates together if accepted. After that, begin R2's
|
||||
pinned complete gate; assign the R4 decision owner in parallel. Do not begin
|
||||
bulk comment, artifact, or giant-file cleanup first.
|
||||
Loading…
Add table
Add a link
Reference in a new issue