Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions DECISIONS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Mammoth.LiteMapper Decision Log

## Issue #58 follow-up: benchmark smoke liveness (2026-10-15)

- Linux fixture ownership uses PID plus the exact kernel start token from procfs stat field 22. Cross-process Process.StartTime comparisons are unsuitable on Linux because .NET caches independently estimated wall-clock boot times (dotnet/runtime#108959). Non-Linux fixtures retain exact UTC start ticks. Missing processes are treated as exited; malformed accessible procfs records fail validation. Keep the orphan-alive assertion and exact identity check without a tolerance.
- The user approved a benchmark-only five-minute process deadline after exact-head Windows CI 38739919081 exceeded the old 60-second limit. This bounds startup, outer build, BenchmarkDotNet generated restore/build, Dry execution, reporting and pipe closure; it is not a throughput gate or a measured runtime percentile. Sample process limits remain 60 seconds.
- Packaging TestProcess keeps complete successful stdout/stderr. Timeout messages retain only the last 4,096 characters of each stream, configured/elapsed time, termination action and cleanup status. Execution and pipe completion share the primary deadline; a separate one-second cleanup wait allows post-kill exit and draining without an unbounded retry.
- A direct process that has already exited cannot provide a reliable portable descendant-termination handle. Bound and cancel local pipe reads, report cleanup incomplete when handles remain open, and do not claim orphan cleanup was confirmed. Fixtures explicitly clean up their own known descendants.
- Preserve the existing assertions, normal-GC coverage, allocation host and symbols/publication checks. The allocation host's separate deadline is unchanged. No product/specification semantics, benchmark algorithms, numeric performance thresholds, workflow gates or system settings change.

## Issue #58: allocation-only GC isolation (2026-10-15)

- No product contract, public API, mapping semantics or performance acceptance policy changes. Test-only reflection dispatch reuses the existing test assembly; emitted mapper code remains static.
- .NET 10 background GC can charge unused allocation-context bytes to GC.GetAllocatedBytesForCurrentThread (runtime issues #134724/#134855). Exact single-batch comparisons can therefore fail without additional mapper allocations. Historical CI lacks per-window GC traces; the runtime cause is strongly supported by isolated controls, not retrospectively proven for every event.
- Disable concurrent GC only in a dedicated allocation child process. Keep normal/default GC in the ordinary correctness host. Preserve exact assertions and observable real-allocation controls; do not add tolerances, change equality direction, discard samples or retry until passing.
- Configuration and child assertion failure must be observable in parent tests. A missing host, invalid child request, nonzero exit or bounded-execution failure cannot silently pass. The mitigation is test infrastructure, not a library performance claim or application configuration recommendation.

## Generator symbol package layout (2026-10-15)

- Contract: specification sections 4.1, 22.19 and 24.6 require analyzer isolation, valid shipping packages and portable reproducible symbols. No public API or mapping semantic change is needed.
Expand Down
16 changes: 16 additions & 0 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Mammoth.LiteMapper Implementation Plan

## Issue #58 follow-up: smoke process deadlines (2026-10-15)

- CI follow-up: correct Linux fixture ownership with the kernel start token, cover procfs command names and malformed records with red-first parser cases, preserve every existing cleanup/liveness assertion, and rerun exact-head Windows/Linux validation. This is test infrastructure only.
- Authorized scope: benchmark smoke only receives a 300-second completion deadline; sample calls retain 60 seconds. Preserve all success assertions, allocation isolation, symbols validation and workflow gates. No local timing benchmarks.
- Test-first: subprocess fixtures cover retained bounded stdout/stderr tails, successful full-output compatibility, direct-child termination and inherited pipes after direct-child exit. Keep finite fixture watchdogs and clean up only owned fixture processes.
- Implementation: drain streams incrementally while retaining complete successful output, add bounded timeout tails and elapsed/cleanup diagnostics, and allow one second after termination request for exit and pipe closure. If inherited descendants cannot be confirmed terminated after the direct child exits, report incomplete cleanup rather than claim success.
- Validation: focused process regressions against the old helper first, repeat focused checks after implementation, broader correctness/package checks excluding the local benchmark launcher, independent static review, then push the same PR and monitor exact-head CI. No merge, closure, publication or machine setting changes.

## Issue #58: isolate allocation-regression execution (2026-10-15)

- Scope: a separate test-only child host with background GC disabled. Keep default GC for ordinary tests, all existing exact assertions, and product/runtime mapping behavior unchanged.
- Test-first: verify effective child configuration, parent isolation, zero and real allocation controls, and propagation of an intentional exact-allocation assertion failure. Exercise boolean data rows and fail closed for invalid child execution.
- Implementation: route only the allocation-sensitive methods through the child, reuse the same test assembly, bound execution and pipe reads, and copy the host with the test build in both solution formats. No tolerance or retry-until-pass.
- Validation: repeated focused allocation checks, complete correctness tests and Roslyn 4.8/4.14/5.9 hosts, independent review, then draft PR to active develop and exact-head CI. Exclude local timing benchmarks; preserve workflows and gates. Packaging work is separate.
- Boundary: isolated feat/codex-allocation-test-isolation originally branched from develop a0e21684000653ae7445d9352e22df0fbd02f642 after 4.0.1 publication, then rebased onto develop c3ef53ec3762d825c2945cbc6394af310bda1933 after merged symbols PR #59. No merge, tag, publication or machine setting changes.

## Generator symbol package repair (2026-10-15)

- Scope: user-authorized packaging maintenance on an isolated branch from develop after the 4.0.1 release. Prepare corrected 4.0.1 symbols separately, then open a draft PR against develop; the release branch has been removed. This is direct maintenance, not a claimed tracker item.
Expand Down
15 changes: 15 additions & 0 deletions Mammoth.LiteMapper.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mammoth.LiteMapper.Generato
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mammoth.LiteMapper.AllocationTestHost", "tests\Mammoth.LiteMapper.AllocationTestHost\Mammoth.LiteMapper.AllocationTestHost.csproj", "{0D2AF503-DF26-4ED3-9BCD-87A27875F620}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mammoth.LiteMapper.Generator.Tests", "tests\Mammoth.LiteMapper.Generator.Tests\Mammoth.LiteMapper.Generator.Tests.csproj", "{490B9D73-BD11-409A-AD32-5AC2D4946CF3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mammoth.LiteMapper.Runtime.Tests", "tests\Mammoth.LiteMapper.Runtime.Tests\Mammoth.LiteMapper.Runtime.Tests.csproj", "{0A429483-44E1-4590-B63A-1A8FBC9CC417}"
Expand Down Expand Up @@ -85,6 +87,18 @@ Global
{674F176D-B422-48A7-8806-342285AE7BB2}.Release|x64.Build.0 = Release|Any CPU
{674F176D-B422-48A7-8806-342285AE7BB2}.Release|x86.ActiveCfg = Release|Any CPU
{674F176D-B422-48A7-8806-342285AE7BB2}.Release|x86.Build.0 = Release|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Debug|x64.ActiveCfg = Debug|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Debug|x64.Build.0 = Debug|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Debug|x86.ActiveCfg = Debug|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Debug|x86.Build.0 = Debug|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Release|Any CPU.Build.0 = Release|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Release|x64.ActiveCfg = Release|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Release|x64.Build.0 = Release|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Release|x86.ActiveCfg = Release|Any CPU
{0D2AF503-DF26-4ED3-9BCD-87A27875F620}.Release|x86.Build.0 = Release|Any CPU
{490B9D73-BD11-409A-AD32-5AC2D4946CF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{490B9D73-BD11-409A-AD32-5AC2D4946CF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{490B9D73-BD11-409A-AD32-5AC2D4946CF3}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -213,6 +227,7 @@ Global
{4B4EC83E-2794-469A-87BB-D152A047BDA9} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{723164FA-7035-46C7-B349-B154ABAC9139} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{674F176D-B422-48A7-8806-342285AE7BB2} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{0D2AF503-DF26-4ED3-9BCD-87A27875F620} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{490B9D73-BD11-409A-AD32-5AC2D4946CF3} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{0A429483-44E1-4590-B63A-1A8FBC9CC417} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{DF2A75F8-6465-4363-8122-D496115D1993} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
Expand Down
1 change: 1 addition & 0 deletions Mammoth.LiteMapper.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<Project Path="src/Mammoth.LiteMapper/Mammoth.LiteMapper.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/Mammoth.LiteMapper.AllocationTestHost/Mammoth.LiteMapper.AllocationTestHost.csproj" />
<Project Path="tests/Mammoth.LiteMapper.Generator.Tests/Mammoth.LiteMapper.Generator.Tests.csproj" />
<Project Path="tests/Mammoth.LiteMapper.IntegrationTests/Mammoth.LiteMapper.IntegrationTests.csproj" />
<Project Path="tests/Mammoth.LiteMapper.Packaging.Tests/Mammoth.LiteMapper.Packaging.Tests.csproj" />
Expand Down
17 changes: 17 additions & 0 deletions STATUS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Mammoth.LiteMapper Status

## Issue #58 follow-up: smoke timeout diagnostics (2026-10-15)

- CI correction: b9ae890 passed full Windows validation and all six Roslyn jobs; Linux passed 1,392 cases and failed only the new orphan fixture's cross-process StartTime ownership check. Linux .NET estimates boot time separately per process, making exact converted UTC ticks unsuitable. Fixture ownership now compares the exact kernel start token on Linux and retains UTC ticks elsewhere, without weakening PID reuse protection or the orphan-alive assertion. Seven procfs parser cases fail before implementation; the integrated process set passes three complete local runs (42/42), broader safeguards pass 255/255, and the Release packaging-test project builds without warnings/errors. No test failures/skips. Linux execution awaits corrected-head CI.
- User-approved scope: only BenchmarkHarnessRunsDryJobAndRecordsPinnedComparisons receives a 300-second process deadline; sample calls retain 60 seconds. All existing success assertions, allocation isolation, symbols/publication validation, benchmark algorithms and workflow settings remain unchanged.
- Red-first: all three new subprocess cases fail against the old helper while four existing controls pass. Integrated tests retain full successful 1 MiB stderr output, bound timeout tails to 4,096 characters per stream, confirm direct/tree cleanup and distinguish an exited parent with inherited pipe holders.
- Implementation: incremental concurrent capture, one execution deadline for process exit and both pipes, then at most one second for cleanup/draining after a termination request. Timeout diagnostics include configured/elapsed time, termination action and cleanup status. When the direct process already exited, descendant termination remains unconfirmed and incomplete cleanup is reported explicitly. Test fixtures clean up only recorded PID/start-time identities.
- Validation: Release solution build passes with zero errors and two existing transitive dependency audit warnings. Final process regressions pass three complete repetitions (21/21), allocation preservation 8/8, and broader process/release/publication safeguards 248/248, zero failures/skips. Independent read-only static review is clean after correcting Windows fixture handle inheritance and ordering readiness before the new fixture deadlines.
- Limits and delivery: local timing benchmarks and nested real-build/package-launching cases are excluded; final full Windows/Linux, Roslyn, package, trim and required Native AOT evidence belongs to the exact-head PR CI. Five minutes is a bounded smoke liveness budget, not a measured percentile or performance gate. Update existing PR #60 for human review only; no merge, closure, publication or machine/system settings changes.

## Issue #58: isolated allocation regressions (2026-10-15)

- Scope: test-only allocation host disables background GC, validates effective configuration, reuses the selected existing assertion body once and fails the parent on child errors or a bounded timeout. Only three existing methods/four rows dispatch through it. Exact assertions, default-GC functional coverage, product sources, workflows and performance gates are unchanged.
- Test-first evidence: four control cases fail before isolation. The integrated focused set passes 8/8, followed by ten complete repetitions (80/80), zero failures/skips. Exact controls distinguish 0 and 64 bytes; the intentionally incorrect zero-allocation assertion fails in the child and its unique message plus real allocation count reach the parent. A copied-runtimeconfig negative probe fails before invocation when concurrent GC is enabled, then restores the original bytes.
- Correctness: Release solution build and both solution path lists pass. Full generator suites pass 1,091/1,091 each on Roslyn 4.8.0, 4.14.0 and 5.9.0; runtime 5/5 and integration 1/1 pass, all with zero failures/skips. Independent read-only static review is clean after correcting the test-host dependency closure. Two pre-existing transitive System.Security.Cryptography.Pkcs 9.0.6 audit warnings remain (NU1902/NU1903).
- Limits: the full local solution-test attempt was stopped after packaging stalled in its first dotnet pack command; completed correctness TRX results are retained, and packaging is not represented as complete. Its separate repair is out of scope. No local benchmark timing runs. The mitigation does not prove throughput or retrospectively establish every historical CI spike's cause.
- Delivery: isolated feat/codex-allocation-test-isolation originally branched from develop a0e21684000653ae7445d9352e22df0fbd02f642 after 4.0.1 publication, then rebased onto develop c3ef53ec3762d825c2945cbc6394af310bda1933 containing merged symbols PR #59. PR #60 remains open for human review; current exact-head CI evidence is recorded there. No merge, tag, publication or persistent machine-setting changes.

## Generator symbol packaging checkpoint (2026-10-15)

- Scope: direct user-authorized maintenance from develop after 4.0.1 publication. The old release branch is absent. The isolated fix branch targets develop and stops at draft PR/human review.
Expand Down
23 changes: 23 additions & 0 deletions docs/ALLOCATION_TESTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Allocation regression tests

The generator test suite includes exact allocation-count regressions for recursive mapping. Run them through MSTest as usual:

```sh
dotnet restore Mammoth.LiteMapper.sln
dotnet build Mammoth.LiteMapper.sln -c Release --no-restore
dotnet test tests/Mammoth.LiteMapper.Generator.Tests/Mammoth.LiteMapper.Generator.Tests.csproj -c Release --no-build --no-restore --filter "TestCategory=Allocation"
```

The allocation-sensitive test methods dispatch to the test-only `Mammoth.LiteMapper.AllocationTestHost` process. Its runtime configuration disables background GC (`System.GC.Concurrent=false`). The ordinary generator test host keeps its default GC configuration, and the other correctness tests continue to run there. This configuration does not change LiteMapper packages or applications.

The child loads the same test assembly and runs the existing assertions once. Boolean data rows are forwarded explicitly. Assertion failures, missing methods, invalid configuration, nonzero process exits and timeouts fail the parent test; there is no retry, tolerance or selection of the lowest measurement. Standard output and error are returned to MSTest for diagnosis.

## Why isolation is necessary

[dotnet/runtime#134724](https://github.com/dotnet/runtime/issues/134724) reports phantom `GC.GetAllocatedBytesForCurrentThread` deltas under background GC, including Windows and Ubuntu x64. The [proposed runtime fix](https://github.com/dotnet/runtime/pull/134855) retires unused allocation-context space when background GC clears an abandoned context. This is CLR accounting behavior; the Roslyn version in the CI matrix selects a compiler, while these tests execute on .NET 10.

The investigation reproduced the counter anomaly in a package-free .NET 10.0.13 program without LiteMapper or Roslyn. Exact historical CI failures did not capture GC traces, so the matching runtime issue is strong supporting evidence rather than a retrospective proof of each spike. Disabling JIT tiering or PGO did not eliminate the local test failures. Extra warm-up, retries or a wider allocation budget do not address the defective accounting path.

The child verifies its effective GC configuration before executing tests. Regression controls also verify that ordinary-host GC remains unchanged, zero-allocation work reads zero, a real heap allocation is counted exactly, and an intentional exact-allocation assertion failure reaches the parent as a failed assertion. The existing tracker size, promotion, spill-reuse and traversal assertions remain exact.

This mitigation does not establish throughput or latency performance and is not a substitute for functional cycle-tracking tests under normal GC. It also does not make arbitrary allocation measurements deterministic: allocations must stay outside measurement windows unless they are the behavior being checked. Reassess the mitigation against a verified runtime fix before removing it; retain the positive controls and exact regression assertions.
2 changes: 2 additions & 0 deletions docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Use Mammoth.LiteMapper to declare compile-time mappings, configure conversions,

This guide describes Mammoth.LiteMapper 4.0.1. The installation examples pin that version, and the [compiling samples](../samples/) demonstrate the supported mappings. See the [4.0.1 changelog](../CHANGELOG.md#401) for the patch changes and the [migration guide](#versions-and-upgrading) for earlier releases.

Repository contributors can follow the [allocation regression validation guide](ALLOCATION_TESTS.md). Its GC isolation applies only to test processes; applications need no GC configuration for LiteMapper. Contributor process validation gives the BenchmarkDotNet smoke test a five-minute completion deadline and sample runs 60 seconds; timeout diagnostics include bounded output tails and cleanup status. These are process liveness checks, not throughput gates.

Runnable examples:

- `samples/Mammoth.LiteMapper.Samples.Basic/Program.cs`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<IsTestProject>false</IsTestProject>
<IsPackable>false</IsPackable>
<UseAppHost>false</UseAppHost>
<ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>
</PropertyGroup>
</Project>
Loading
Loading