Repository navigation
fix(#890): the canary measures whether a poisoned process can emit AT ALL - #3613
Merged
Merged
Conversation
… ALL Three unanimous `dissect=READS-HEALTHY` readings closed the read-probe direction, and a fourth arrived today (Plugins run 34110361260, shard 3, 94 readings). Leg 3 reaches the symbols by reflection, so it exercises `get_ContainingTypeDefinition`'s own compiled body from a caller that is not `MetadataWriter` — if the fault is code that is wrong only where the writer reaches it, leg 3 answers READS-HEALTHY BY CONSTRUCTION. No further read probe narrows anything. Leg 4 stays inside a real Emit and varies ONE thing instead: whether the compilation contains a nested type at all. `FlatCanarySource` is a single top-level, non-generic, member-less class, emitted against the SAME references as leg 1 (leg 2 varies the references; leg 4 varies the source, so the two are not confounded). `MetadataWriter.GetConsolidatedTypeParameters` opens with `typeDef.AsNestedTypeDefinition(Context)` and returns immediately when that answers null, so for a top-level type the recursion — and the `ITypeDefinitionMember.ContainingTypeDefinition` call every #890 stack dies in — is never reached. With no members either, that getter's `NamedTypeSymbol` overload has exactly one caller left: `AsNestedTypeDefinitionImpl`'s guard having answered TRUE for a type whose containing type is null by construction. flat=EMITS the process is NOT emit-dead; the fault needs the nested walk flat=SAME-FRAME the writer reached the frame with no nested type anywhere ⇒ the guard read TRUE where it must read FALSE. #890 in ONE method — the dotnet/runtime report flat=OTHER-FRAME two frames are two faults; both named, nothing concluded flat=INCONCLUSIVE / NOT-RUN / UNAVAILABLE — an absent reading stays visible This replaces an assumption load-bearing since the canary was written — "a flat class would emit fine even on a poisoned writer" — which was never measured and is why every occurrence has been read as PROCESS CANNOT EMIT, a claim about emit as such on evidence that only ever exercised one shape. Gated exactly like leg 3 (BELOW-ROSLYN and DIVERGENT, never REFERENCES, where the pristine leg emitted and the question does not arise). Cost is one trivial in-memory emit on an already-failed path. Mutation-proven: the discriminator holds only while the flat source has no nested type, no arity and no members — a field added "for realism" is a second ITypeDefinitionMember and `flat=SAME-FRAME` silently stops meaning what it says. `EmitCanaryFlatLegTest` asserts the shape off Roslyn's own binding, not the literal. Falsification: dropping the site comparison reds AFlatEmitDyingSOMEWHEREELSE_ConcludesNothing; adding a nested type reds the shape guard; dropping the append reds three. Docs conserve both of today's investigations rather than leaving them in issue threads: NodeTypeCompilation gains leg 4, the 2026-09-07 occurrence, and the precursor search (a cancelled Roslyn emit, a gen2 GC and ALC churn are each refuted against the 09-06 occurrence — and the poisoning event is not visible in any sink the platform writes). WriteVerdictTotality and DebuggingMessageFlow record the #2543 measurement: 25 CD bake jobs since #3480, all descended from its merge, 5 carrying PATCH_ECHO_SEEN stall trails, PATCH_FLUSH_FAULTED_SYNC zero — the synchronous-throw arm is refuted and `flush(committed)` blocking is what is left. Refs #890, #2543. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 7, 2026
…ts own claim 'the broken state is below Roslyn' and 'PROCESS CANNOT EMIT' are read together, and flat=EMITS narrows the second one. A reader who sees the two lines side by side without being told which qualifies which gets a contradiction and no guidance — which is the failure mode this verdict keeps having to remove. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The changes are additive, well-guarded by tests, and constrained to diagnostic behavior plus documentation without introducing async/concurrency risks or breaking existing API surfaces.
Pull request overview
This PR strengthens the Roslyn emit “canary” diagnostics for issue #890 by adding a new “flat=” leg that performs an additional minimal in-process emit to distinguish “emit is dead” from “the nested/generic metadata-writer walk is dead,” and documents the new discriminator and recent measurements.
Changes:
- Add a new flat-source canary leg (
flat=) to the emit verdict pipeline and surface its reading in theBELOW-ROSLYN/DIVERGENTverdict text. - Add a dedicated test contract (
EmitCanaryFlatLegTest) to guard the flat source’s shape and the classifier semantics. - Document leg 4 and related observations in the architecture docs, and add a user-facing What’s New entry describing the improved failure message.
File summaries
| File | Description |
|---|---|
| test/MeshWeaver.Compiler.Pipeline.Test/EmitCanaryFlatLegTest.cs | Adds tests that pin the flat-leg source shape and the expected flat= classification behavior. |
| src/MeshWeaver.Compiler/EmitPipeline.cs | Implements the FlatCanarySource, threads a flat= probe into ProbeSharedEmitState/Verdict, and adds ClassifyFlatLeg + safe probe execution. |
| src/MeshWeaver.Documentation/Data/WhatsNew/2026-09-07-a-compile-failure-now-says-how-far-the-damage-goes.md | Adds a What’s New entry explaining the new “simplest possible build” reading to end users. |
| src/MeshWeaver.Documentation/Data/Architecture/NodeTypeCompilation.md | Documents the new leg 4 discriminator and the 2026-09-07 occurrence context. |
| src/MeshWeaver.Documentation/Data/Architecture/WriteVerdictTotality.md | Records the post-#3480 negative measurement and narrows the remaining hypothesis. |
| src/MeshWeaver.Documentation/Data/Architecture/DebuggingMessageFlow.md | Updates the PATCH_ECHO_SEEN row based on measured behavior and the new negative evidence. |
Review details
- Files reviewed: 6/6 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Contributor
Contributor
Contributor
Contributor
Contributor
Contributor
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four unanimous
dissect=READS-HEALTHYreadings — the fourth arrived this morning onPlugins run 34110361260
shard 3, 94 of them — say the same thing, and they say it by construction: leg 3 reaches the
symbols by reflection, so it exercises
get_ContainingTypeDefinition's own compiled body from acaller that is not
MetadataWriter. If the fault is code that is wrong only where the writer reachesit — which is exactly what those readings point at — leg 3 answers
READS-HEALTHYno matter howbroken the process is. A fifth read probe narrows nothing.
Leg 4 — vary the NESTING, inside a real emit
FlatCanarySourceis a single top-level, non-generic, member-less class, emitted against thesame references as leg 1 (leg 2 varies the references; leg 4 varies the source, so the two are
not confounded), in the same process, microseconds later.
MetadataWriter.GetConsolidatedTypeParametersopens withtypeDef.AsNestedTypeDefinition(Context)and returns immediately when that answers null, so for a top-level type the recursive overload — and
with it the
ITypeDefinitionMember.ContainingTypeDefinitioncall every #890 stack dies in — is neverreached. With no members either, that getter's
NamedTypeSymboloverload has exactly one caller leftin that emit:
AsNestedTypeDefinitionImpl's guard having answered TRUE for a type whose containingtype is null by construction.
flat=EMITSPROCESS CANNOT EMITis true of the workload and false of emit as suchflat=SAME-FRAME@…dotnet/runtimereportflat=OTHER-FRAME@…flat=INCONCLUSIVE(…)/NOT-RUN/UNAVAILABLE(…)Both branches are informative and neither was previously observable. This replaces an assumption
that has been load-bearing since the canary was written — the source comment asserted "a flat class
would emit fine even on a poisoned writer", which was never measured, and which is why every one of
~30 occurrences has been read as a claim about emit as such on evidence that only ever exercised
one shape.
Gated exactly like leg 3:
BELOW-ROSLYNandDIVERGENT, neverREFERENCES(there the pristine legemitted, so the process demonstrably can emit). Cost is one trivial in-memory emit on a path that has
already failed.
Mutation-proven, because the discriminator is silent when it dies
flat=SAME-FRAMEmeans what it says only while the flat source has no nested type, no genericarity and no members — a field added "for realism" is a second
ITypeDefinitionMember, i.e. asecond caller of a
ContainingTypeDefinitiongetter, and the verdict quietly stops being adiscriminator.
EmitCanaryFlatLegTestasserts the shape off Roslyn's own binding(
SourceModule.GlobalNamespace,ContainingType,Arity, members), never by matching the literal,so a rewrite that preserves the shape passes and one that does not cannot. It also pins the positive
control: the flat source must EMIT on a healthy process, or every
flat=reading in CI measures thistest's bug.
Falsification, each reverted alone:
flat=SAME-FRAMEfor any throw)AFlatEmitDyingSOMEWHEREELSE_ConcludesNothingTheFlatSource_HasNothingThatCouldReachTheFrameExceptTheGuardMeshWeaver.Compiler.Pipeline.Test700/700,MeshWeaver.Documentation.Test320/320, everytouched project
-c Release -warnaserrorat0 Warning(s) 0 Error(s).Docs — both of today's investigations are conserved here, not in the issue threads
NodeTypeCompilation.mdgains leg 4, the 2026-09-07 occurrence, and the precursor search this PRcame out of. Read from the authoritative sink (pid
3039, onset10:29:50.936, 72.0 s / 167TEST_STARTs into the host, inside a test that then PASSED), three candidate poisoning events werechecked against the 2026-09-06 21:02Z occurrence and all three are refuted: a deliberately
cancelled Roslyn
Emit(CompileLegBoundWedgeTestfired 17.4 s before onset on 09-07; zero suchtimeouts before onset on 09-06), a gen2 GC (51→53 in the 800 ms before onset on 09-07;
gc2=16unchanged for 4.5 s on 09-06), and ALC churn (
alc=4 asm=149vsalc=1 asm=153). The warm-upspread is now 4× wider than the figure two doc pages quoted — 33 s/12, 55 s/48, 72 s/167, 130 s/199 —
so onset timing constrains nothing. The honest conclusion is a limit: the poisoning event is not
visible in any sink the platform writes, because the trace log records only faults and the last
fault before onset differs completely between occurrences.
WriteVerdictTotality.md+DebuggingMessageFlow.mdrecord the #2543 measurement #3480 was builtfor: 25 core CD bake jobs from #3480's merge to 13:31Z today, every head commit verified
merge-base --is-ancestor, 7GATE FAILED, 5 carryingPATCH_ECHO_SEENstall trails (43·19·8·4·4)— and
PATCH_FLUSH_FAULTED_SYNCfired 0 times. The trail keeps a sliding tail, so the absence isreal. The synchronous-throw form of arm 1 is refuted; what is left is
flush(committed)blocking,and the next discriminator is one stage (
PATCH_FLUSH_RESOLVED), not another sweep.Refs #890, #2543. Nothing here claims to fix either — #890 is below Roslyn, and #2543 now has a
narrower arm than it had this morning.
🤖 Generated with Claude Code