@@ -26,11 +26,13 @@ down before anyone re-derives it:
2626
2727So the static half of "enterprise lifetime checking" is not a gap — it's shipped.
2828What's actually missing is the ** dynamic/runtime half** : a lightweight guard
29- that fails loudly * at run time* for exactly the two cases static analysis
30- structurally cannot close — D5 (ownership transferred through an unmodeled
31- callee) and cross-thread disposal races (both explicit non-goals of P-005) —
32- plus a cheap, cross-platform substitute for OwnAudit's ClrMD heap walk that
33- runs in an ordinary unit test, no Windows stand required.
29+ that fails loudly * at run time* when misuse actually happens — a double-dispose
30+ or a post-dispose access, whether it originates from an ownership hand-off
31+ own-check doesn't model (P-005 D5) or from a cross-thread race (both explicit
32+ non-goals of P-005) — plus a cheap, ClrMD-independent test-time check that runs
33+ in an ordinary unit test, no Windows stand required. Neither piece models * why*
34+ an object ended up misused, the way OwnAudit's phase 5 or a static prover would;
35+ they only make the misuse itself loud the moment it happens.
3436
3537## What already exists (do not re-derive)
3638
@@ -71,10 +73,11 @@ Two small, independent, opt-in runtime helpers (a "diagnostic mode", not a
7173shipped allocator):
7274
7375** 1. ` LifetimeGuard ` base / wrapper — loud instead of silent.**
74- A ` DEBUG ` /` TEST ` -only ` IDisposable ` base that turns the two silent failure
75- modes static analysis cannot close (D5 unknown transfer, cross-thread race)
76- into an immediate ` ObjectDisposedException ` /` InvalidOperationException ` instead
77- of corrupting state quietly:
76+ A ` DEBUG ` /` TEST ` -only ` IDisposable ` base that turns silent misuse — a
77+ double-dispose, or an access after ` Dispose() ` — into an immediate
78+ ` ObjectDisposedException ` /` InvalidOperationException ` instead of corrupting
79+ state quietly, regardless of whether the root cause was an unmodeled
80+ ownership hand-off (P-005 D5) or a cross-thread race:
7881
7982``` csharp
8083public abstract class LifetimeGuard : IDisposable
@@ -94,8 +97,11 @@ public abstract class LifetimeGuard : IDisposable
9497
9598This does not replace ` OWN002 ` /` OWN003 ` — those catch the mistake at compile
9699time, for free, when the pattern is intraprocedural. ` LifetimeGuard ` catches
97- what's left: ownership handed through a callee own-check doesn't model
98- (P-005 D5), and the cross-thread race P-005 explicitly declines to touch.
100+ what's left: it surfaces misuse * at the point it happens* , even when the
101+ underlying cause was ownership handed through a callee own-check doesn't model
102+ (P-005 D5) or the cross-thread race P-005 explicitly declines to touch — it
103+ does not itself model or detect the hand-off/race, only the resulting
104+ double-dispose or post-dispose access.
99105
100106** 2. Disposal quarantine for tests — a ClrMD-free complement to phase 5.**
101107An opt-in ` ITrackedDisposable ` + an ambient registry that records the
@@ -129,8 +135,11 @@ debug assertion than an auditor.
129135- Not a production-safe pattern as-is: throwing from ` Dispose() ` is a real
130136 behavior change (already-suppressed ` Dispose ` exceptions in ` finally ` /` using `
131137 chains can mask the original exception) — ` LifetimeGuard ` must ship
132- ` DEBUG ` /` TEST ` -gated (e.g. ` [Conditional] ` on the throw, or a config flag),
133- never silently opt production code into new exceptions.
138+ ` DEBUG ` /` TEST ` -gated: wrap the throw in ` #if DEBUG ` / ` #if TEST ` , or route it
139+ through a small helper method decorated ` [Conditional("DEBUG")] ` (the
140+ attribute only applies to methods, not to a bare ` throw ` statement), or gate
141+ it behind a config flag — never silently opt production code into new
142+ exceptions.
134143- Not a replacement for ` OWN002 ` /` OWN003 ` (compile-time, zero runtime cost,
135144 works before the code ever ships) or for OwnAudit phase 5 (ground-truth heap
136145 retention) — it fills the gap between them: cases neither can see, at the
0 commit comments