Skip to content

fix(#890): the canary measures whether a poisoned process can emit AT ALL - #3613

Merged
meshweaver-cloud[bot] merged 2 commits into
mainfrom
fix/890-flat-emit-leg
Sep 7, 2026
Merged

meshweaver-cloud[bot] merged 2 commits into
mainfrom
fix/890-flat-emit-leg

Conversation

@rbuergi

@rbuergi rbuergi commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Four unanimous dissect=READS-HEALTHY readings — the fourth arrived this morning on
Plugins 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 a
caller that is not MetadataWriter. If the fault is code that is wrong only where the writer reaches
it — which is exactly what those readings point at — leg 3 answers READS-HEALTHY no matter how
broken the process is. A fifth read probe narrows nothing.

Leg 4 — vary the NESTING, inside a real emit

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), in the same process, microseconds later.

MetadataWriter.GetConsolidatedTypeParameters opens with typeDef.AsNestedTypeDefinition(Context)
and returns immediately when that answers null, so for a top-level type the recursive overload — and
with it 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
in that emit: AsNestedTypeDefinitionImpl's guard having answered TRUE for a type whose containing
type is null by construction.

verdict what it says
flat=EMITS the process is NOT emit-dead — the fault needs the nested/generic walk, the guard is intact, and PROCESS CANNOT EMIT is true of the workload and false of emit as such
flat=SAME-FRAME@… 🚨 the writer reached that frame with no nested type anywhere in the compilation ⇒ the guard read TRUE where it must read FALSE. #890 in ONE method — no recursion, no generics, no nesting. That is the dotnet/runtime report
flat=OTHER-FRAME@… two frames are two faults until shown otherwise; both sites printed, nothing concluded
flat=INCONCLUSIVE(…) / NOT-RUN / UNAVAILABLE(…) it could not be compared / no probe / the probe faulted — an absent reading stays visible as absent

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-ROSLYN and DIVERGENT, never REFERENCES (there the pristine leg
emitted, 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-FRAME means what it says only while the flat source has no nested type, no generic
arity and no members
— a field added "for realism" is a second ITypeDefinitionMember, i.e. a
second caller of a ContainingTypeDefinition getter, and the verdict quietly stops being a
discriminator. EmitCanaryFlatLegTest asserts 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 this
test's bug.

Falsification, each reverted alone:

reverted reds
the site comparison (flat=SAME-FRAME for any throw) AFlatEmitDyingSOMEWHEREELSE_ConcludesNothing
the flat source gains a nested type TheFlatSource_HasNothingThatCouldReachTheFrameExceptTheGuard
the reading is not appended to the verdict 3 tests

MeshWeaver.Compiler.Pipeline.Test 700/700, MeshWeaver.Documentation.Test 320/320, every
touched project -c Release -warnaserror at 0 Warning(s) 0 Error(s).

Docs — both of today's investigations are conserved here, not in the issue threads

NodeTypeCompilation.md gains leg 4, the 2026-09-07 occurrence, and the precursor search this PR
came out of. Read from the authoritative sink (pid 3039, onset 10:29:50.936, 72.0 s / 167
TEST_STARTs
into the host, inside a test that then PASSED), three candidate poisoning events were
checked against the 2026-09-06 21:02Z occurrence and all three are refuted: a deliberately
cancelled Roslyn Emit (CompileLegBoundWedgeTest fired 17.4 s before onset on 09-07; zero such
timeouts before onset on 09-06), a gen2 GC (51→53 in the 800 ms before onset on 09-07; gc2=16
unchanged for 4.5 s on 09-06), and ALC churn (alc=4 asm=149 vs alc=1 asm=153). The warm-up
spread 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.md record the #2543 measurement #3480 was built
for: 25 core CD bake jobs from #3480's merge to 13:31Z today, every head commit verified
merge-base --is-ancestor, 7 GATE FAILED, 5 carrying PATCH_ECHO_SEEN stall trails (43·19·8·4·4)
— and PATCH_FLUSH_FAULTED_SYNC fired 0 times. The trail keeps a sliding tail, so the absence is
real. 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

… 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>
…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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 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 the BELOW-ROSLYN / DIVERGENT verdict 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.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

492 tests  ±0   492 ✅ ±0   40s ⏱️ +3s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit dbf31d1. ± Comparison against base commit 5453be4.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

1 061 tests  +10   1 061 ✅ +10   1m 23s ⏱️ +6s
    2 suites ± 0       0 💤 ± 0 
    2 files   ± 0       0 ❌ ± 0 

Results for commit dbf31d1. ± Comparison against base commit 5453be4.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

251 tests  ±0   251 ✅ ±0   2m 12s ⏱️ ±0s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit dbf31d1. ± Comparison against base commit 5453be4.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

1 642 tests  ±0   1 448 ✅ ±0   1m 59s ⏱️ -10s
    4 suites ±0     194 💤 ±0 
    4 files   ±0       0 ❌ ±0 

Results for commit dbf31d1. ± Comparison against base commit 5453be4.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

    5 files  ±0      5 suites  ±0   2m 24s ⏱️ -9s
1 578 tests ±0  1 578 ✅ ±0  0 💤 ±0  0 ❌ ±0 
1 579 runs  ±0  1 579 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit dbf31d1. ± Comparison against base commit 5453be4.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

1 142 tests  ±0   1 142 ✅ ±0   4m 11s ⏱️ -2s
    3 suites ±0       0 💤 ±0 
    3 files   ±0       0 ❌ ±0 

Results for commit dbf31d1. ± Comparison against base commit 5453be4.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Test Results

   16 files  ± 0     16 suites  ±0   12m 50s ⏱️ -13s
6 166 tests +10  5 972 ✅ +10  194 💤 ±0  0 ❌ ±0 
6 167 runs  +10  5 973 ✅ +10  194 💤 ±0  0 ❌ ±0 

Results for commit dbf31d1. ± Comparison against base commit 5453be4.

@meshweaver-cloud
meshweaver-cloud Bot added this pull request to the merge queue Sep 7, 2026
Merged via the queue into main with commit 3e3a10c Sep 7, 2026
29 checks passed
@rbuergi
rbuergi deleted the fix/890-flat-emit-leg branch October 10, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants