Repository navigation
fix: a framework roll is FrameworkStale, not a phantom dependency drift - #2492
Conversation
`NodeTypeBakeStatus.Classify` checked the per-type dependency record BEFORE
comparing the record's framework identity to the live one, and the two are not
independent: the reserved `!toolchain` entry hashes the
`FrameworkBuildIdentity.FullMvidAssemblies` closure's implementation MVIDs, and
`FrameworkVersion` folds those SAME MVIDs (all 16 closure members are
`ContentSurfaceAssemblies` members — both sets are pinned by
`FrameworkBuildIdentityTest`). A framework roll therefore moves the record by
construction, the dependency check always fires first, and `FrameworkStale` —
documented as "the ordinary every-deploy state … ABI safety, not a bug" — became
unreachable for any record-stamped type.
Measured on memex-cloud 2026-08-27: 273 of 273 uncovered NodeTypes reported
`DependencyStale` ("a bound module/toolchain dependency changed") and ZERO
reported `FrameworkStale`, while the only thing that had changed was the image —
the types carried framework `sceaefc9…` / `!toolchain mvid:bc0ed79d…` against a
live `s66ba35f…` / `mvid:f8a7cc07…` (the value `Store/Plugin` stamped when it
compiled on this image the same morning).
Second defect, same line: the mismatch reason was computed and thrown away.
`Classify` asked `FindMismatch(…) is not null` and `Describe` printed one fixed
sentence, so the string that says WHICH dependency moved never reached an
operator. Three investigations across three repos (MeshWeaver.Education#216,
MeshWeaver.SocialMedia#75, MeshWeaver.Plugins#723) each read that sentence as
evidence of a module drift and went hunting one that did not exist.
- A record mismatch the framework identity already accounts for is classified
`FrameworkStale`; reaching `DependencyStale` now means the framework held still
and a binding genuinely moved.
- `ClassifyDetailed` returns the first mismatch, and `DependencyStale`'s detail
names it instead of gesturing at it.
The DECLINE is unchanged. Both states are `NeedsBake` and `WasHealthy`, the
dependency check still preempts the bytes-win rule, and `IsAlreadyAdopted` still
requires `Baked` — exactly the same NodeTypes rebuild, at the same time. Only the
name and the diagnostics change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a misclassification/diagnostics problem in the NodeType bake-state reporting: after a framework roll, NodeTypes were being labeled as DependencyStale (suggesting a module/toolchain drift) even when the framework identity alone explains the dependency-record change. It also restores actionable detail by propagating the specific dependency mismatch string instead of discarding it.
Changes:
- Reorders bake-state attribution so a dependency-record mismatch that coincides with a framework roll is reported as
FrameworkStale, reservingDependencyStalefor cases where the framework identity did not move. - Introduces
ClassifyDetailedto preserve and surface the first dependency mismatch reason, and uses it in store-probing/reporting output. - Adds tests that pin the framework-roll classification and assert that mismatch diagnostics name the moved dependency; adds a WhatsNew entry documenting the user-visible reporting change.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| test/MeshWeaver.Graph.Test/CompiledDependencyRecordTest.cs | Adds regression tests covering framework-roll attribution and mismatch-detail propagation. |
| src/MeshWeaver.Graph/Configuration/NodeTypeBakeStatus.cs | Implements ClassifyDetailed, fixes classification attribution, and improves DependencyStale/FrameworkStale diagnostics. |
| src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-27-bake-state-names-the-framework-roll.md | Documents the corrected stale-state reporting and improved drift diagnostics. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Test Results (shard 2)1 001 tests 809 ✅ 6m 13s ⏱️ Results for commit c31b992. |
Test Results (shard 3)1 078 tests 1 078 ✅ 17m 1s ⏱️ Results for commit c31b992. |
Test Results (shard 4) 7 files 7 suites 10m 39s ⏱️ Results for commit c31b992. |
Test Results (shard 0)1 379 tests 1 379 ✅ 6m 21s ⏱️ Results for commit c31b992. |
Test Results (shard 1)849 tests 845 ✅ 6m 30s ⏱️ Results for commit c31b992. |
Test Results (shard 5)1 579 tests 1 578 ✅ 9m 46s ⏱️ Results for commit c31b992. |
Test Results 45 files 45 suites 56m 31s ⏱️ Results for commit c31b992. |
The finding
Investigating "prebuilt bundle adoption fails even when a correct bundle exists" across
Education#216 / SocialMedia#75 / Plugins#723. The single-predicate hypothesis is refuted — no
predicate declines a correct bundle. What is real is a mislabel that made three unrelated problems
look like one.
Measurement (memex-cloud,
memex-portal-ai:3.0.0-rc8.ci.6028, 2026-08-27)grep -c 'dependency record mismatch'over 48 h of logs on both prod portals: 0. ZeroDECLINEDlines of any kind. The seeder's dependency gate has not declined a bundle in production.framework-stale, and they said so:
AgenticBusiness/MenuandClaims/ClaimcarrycompiledFrameworkVersion=sceaefc9…,!toolchain=mvid:bc0ed79d…, against a lives66ba35f…/mvid:f8a7cc07…— the valueStore/Pluginstamped compiling on this image the same morning.Why they were mislabeled
Classifychecked the dependency record before the framework comparison, and the two are notindependent.
!toolchainhashes theFullMvidAssembliesclosure's impl MVIDs;FrameworkVersionfolds those same MVIDs — all 16 closure members are
ContentSurfaceAssembliesmembers, and both setsare pinned by
FrameworkBuildIdentityTest. So a framework roll moves the record by construction,the dependency check always wins the race, and
FrameworkStale— "the ordinary every-deploy state …ABI safety, not a bug" — is unreachable for any record-stamped type. 273/273 vs 0/273 is that
theorem showing up in a log.
Compounding it, the mismatch reason was computed and discarded:
Describeprinted one fixedsentence, so the string naming which dependency moved never reached anyone.
The fix
FrameworkStale. ReachingDependencyStalenow means the framework held still and a binding genuinely moved.ClassifyDetailedreturns the first mismatch;DependencyStale's detail names it.The decline is unchanged. Both states are
NeedsBake+WasHealthy, the dependency check stillpreempts the bytes-win rule,
IsAlreadyAdoptedstill requiresBaked. Same NodeTypes rebuild at thesame time — only the name and the diagnostics change.
What this does and does not explain
DependencyStaleon 93/101latestAssemblyCollection: "local"isFileSystemAssemblyStore.FileSystemCollectionName, set byPutWithLocationfor adopted and compiled builds alike — never evidence of non-adoption.DECLINED: 0. Its own thread root-caused it: the 60 sTimeoutinPackageInstaller.SeedPrebuiltAssemblies.Edu/CourseCataloggate failuresOk, framework- and toolchain-current.compile=Ok render=ok tests=FAILEDis downstream of loading.Testing
dotnet build -c Release -warnaserrorclean;MeshWeaver.Graph.Test71/71 pass acrossCompiledDependencyRecordTest,NodeTypeBakeStatusTest,FrameworkBuildIdentityTest,LoadableBuildPredicateTest,NodeTypeCompileStateTest. Two new tests pin the framework-rollattribution and the recovered reason.
🤖 Generated with Claude Code