Repository navigation
feat(#1660 WS3): API-surface framework identity — rebuild only when we need to - #1696
Conversation
…e need to The commit identity (#1687) made the CI bake adoptable but OVER-invalidated: every merge — internal-only included — minted a new identity and rebaked all content (pending=82 on the routine ci.3979 roll). The staleness key is now the API SURFACE content compiles against, so a bake goes stale exactly on a breaking change. * FrameworkBuildIdentity: hosts shipping a meshweaver-surface.manifest (the portals + mw-plugin-test, opt-in MeshWeaverSurfaceManifest=true) resolve s<sha256/2> over the sorted (name, surface-id) pairs of the CANONICAL content-surface set — the bake gate's own 36-assembly framework closure, which is what keeps the hash equal across the bake host and the portals despite different TPAs. The surface-id per assembly is the SHA-256 of its REFERENCE assembly (the compiler's own surface oracle: byte-stable under body-only/private edits, moved by any surface change, internals included for IVT'd assemblies). 🚨 MeshWeaver.Graph contributes its FULL impl MVID — its code (DynamicMeshNodeAttributeGenerator, MeshNodeCompilationService's aggregation/@@-include/NuGet-directive pipeline, CodeQueryResolver) shapes the GENERATED input of every NodeType compile, so a body-only change there rebakes without an API change. Fallbacks: the g<sha> commit stamp (manifest-less CI processes; logged everywhere as provenance), then the Graph MVID (local). All consumers ride the one property; the retention sweep attributes the s-tag; the prewarmer logs identity + provenance. * Directory.Build.props: _MeshWeaverWriteSurfaceManifest (GetFileHash over @(ReferencePathWithRefAssemblies), MeshWeaver.* only) + publish hookup; the multi-arch ProduceReferenceAssembly=false optimization is REMOVED — ref assemblies are load-bearing for the identity now (per-leg bin/obj isolation still kills the PR #552 race). * publish-bake-bundles.sh skips-with-notice when the identity's bundle directory already exists — an internal-only merge is a no-op publish. notify-dependents drops the now-wrong identity payload field. * mw-plugin-test --print-framework-identity: one-line diagnostic driving the REAL resolution path; the proofs use it. Proven via that flag: baseline s9f38b645…; internal body-only edit in MeshWeaver.Data keeps the identity; a public member on MeshWeaver.Layout changes it; a body-only edit in MeshWeaver.Graph (ref-asm byte-stable) changes it via the emitter rule; reverting restores the baseline. Ref-asm oracle proven byte-identical across from-clean rebuilds and under body edits, changed by a public const add. Unit pins for the computation, the canonical list (incl. a csproj-closure drift test), the resolution chain, and the tag grammar. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR refines the framework build identity used to key dynamic NodeType compilation/bake artifacts so that content is rebuilt only when the content-facing API surface changes (rather than on every internal-only framework merge), by introducing a surface-manifest-based s<hash> identity with commit g<sha> retained as provenance.
Changes:
- Adds surface-manifest-driven framework identity resolution (
s<hash>) with fallback to commit stamp (g<sha>) and Graph MVID, and updates consumers/tests accordingly. - Enables surface manifest emission/publish for the bake host and portals, and updates logging/diagnostics to include both identity and provenance.
- Updates CD publish behavior and documentation to reflect surface identity, including skipping bundle upload when the target already has the bundles.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/MeshWeaver.PluginTester/Program.cs | Adds --print-framework-identity diagnostic output. |
| tools/MeshWeaver.PluginTester/MeshWeaver.PluginTester.csproj | Enables surface manifest generation for the bake host. |
| test/MeshWeaver.Graph.Test/FrameworkBuildIdentityTest.cs | Adds unit tests for surface identity computation and process resolution. |
| src/MeshWeaver.Hosting/DynamicTypePreWarmerHostedService.cs | Logs framework identity plus commit provenance at warm-up start. |
| src/MeshWeaver.Graph/Configuration/NodeTypeCompilationHelpers.cs | Switches FrameworkVersion to process identity resolution (manifest → stamp → MVID). |
| src/MeshWeaver.Graph/Configuration/FrameworkBuildIdentity.cs | Implements surface identity (s<hash>) computation, manifest parsing, and process-level resolution. |
| src/MeshWeaver.Graph/Configuration/AssemblyCacheRetention.cs | Extends generation tag recognition to include s-prefixed tags. |
| src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-16-rebuild-only-on-breaking-change.md | Adds What’s New entry describing the new behavior. |
| src/MeshWeaver.Documentation/Data/Architecture/ReleaseProcess.md | Updates release/process docs for the new identity resolution chain. |
| src/MeshWeaver.Documentation/Data/Architecture/PluginPackaging.md | Documents s<hash> / g<sha> / MVID identity shapes and rationale. |
| src/MeshWeaver.Documentation/Data/Architecture/NodeTypeCompilation.md | Updates compilation docs to reflect surface-manifest identity. |
| src/MeshWeaver.Documentation/Data/Architecture/ContinuousDeliveryContract.md | Updates CD contract and dependent notification payload to drop identity. |
| src/MeshWeaver.Documentation/Data/Architecture/CiContentBake.md | Updates CI bake docs to explain adoptability via surface identity. |
| memex/Memex.Portal.Monolith/Memex.Portal.Monolith.csproj | Enables surface manifest generation for the monolith portal. |
| memex/aspire/Memex.Portal.Distributed/Memex.Portal.Distributed.csproj | Enables surface manifest generation for the distributed portal. |
| Directory.Build.props | Adds targets to write/publish the surface manifest; documents ref-assembly requirement. |
| deploy/CHANGE-PROPAGATION.md | Updates change propagation guidance for surface identity behavior. |
| deploy/aks/values.aks.yaml | Updates AKS config comments to reflect API-surface identity semantics. |
| deploy/aks/DEPLOY-RUNBOOK.md | Updates runbook explanation of identity and bake adoption. |
| .github/workflows/main-cd.yml | Updates publish-bake comments and dependent dispatch payload (drops identity). |
| .github/scripts/publish-bake-bundles.sh | Adds “skip publish if already present” behavior for surface-identity directories. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Test Results (shard 2)2 455 tests +8 2 451 ✅ +8 7m 50s ⏱️ -3s Results for commit 3fd2b65. ± Comparison against base commit b969a4d. This pull request removes 6 and adds 14 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
Test Results 65 files ± 0 65 suites ±0 46m 18s ⏱️ +56s Results for commit 3fd2b65. ± Comparison against base commit b969a4d. This pull request removes 6 and adds 15 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
…dentity resolution never faults boot Copilot findings on #1696, both real: 1. The publish skip keyed on "any file exists" — a publish that died after 1 of N bundles froze the identity incomplete forever. The publisher now writes a _complete sentinel (listing the bundle set) strictly LAST; the skip keys on the sentinel alone, and the SAME contract holds on the read side: SeedPublishedRoot seeds only sealed source directories, and only the bundles the sentinel lists — an unsealed or torn publication is refused loudly and the sweep compiles instead (test: PublishedRoot_RefusesUnsealedAndTorn…). 2. ResolveProcessIdentity read the surface manifest unguarded — an unreadable/ torn manifest would fault the boot-path identity Lazy. The chain is now the pure ResolveProcessIdentityWithDiagnostics: any manifest failure degrades to the stamp/MVID fallback with a warning CACHED BESIDE the identity (no mutable static), which the pre-warmer logs where it announces the identity (tests: ProcessIdentity_DegradesToTheFallback_*). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Both review findings were real and are fixed in 3fd2b65:
|
#1660 WS3 refinement — rebuild only when we need to: the API-surface identity
Rides on #1687 (commit-deterministic identity). The commit key made the CI bake adoptable but
OVER-invalidated: every platform merge — internal-only included — minted a new identity and
rebaked all content (measured:
pending=82on the routine ci.3979 roll). Content must rebake onlywhen the API SURFACE it compiles against changed — a breaking change.
The identity (one property, new resolution —
FrameworkBuildIdentity)s<hash>— hosts that ship ameshweaver-surface.manifest(portals + the CI bake host, opt-in
<MeshWeaverSurfaceManifest>true</>): the manifest records,per MeshWeaver compile reference, the SHA-256 of its reference assembly — the compiler's own
surface oracle (byte-stable under body-only/private edits; flips on any public/protected — and,
for IVT'd assemblies, internal — surface change; a hand-rolled API walk would miss the IVT
subtlety, which is why the ref-asm is the oracle). The identity hashes the sorted
(name, surface-id)pairs of a canonical content-surface set — the bake gate's ownframework closure (36 assemblies; content referencing anything outside it cannot pass the gate),
which is what makes the hash EQUAL across the bake host and the portals despite their different
TPAs (the portal carries 26 more MeshWeaver assemblies — Blazor/Orleans/hosting — deliberately
outside the content-staleness key). 🚨 Generator exception:
MeshWeaver.Graphcontributesits FULL implementation MVID — its code (DynamicMeshNodeAttributeGenerator, the skeleton/
aggregation/
@@-include/NuGet-directive pipeline in MeshNodeCompilationService +CodeQueryResolver) shapes the GENERATED INPUT of every NodeType compile, so a body-only change
there must rebake without any API change. (Swept: the only other generator, SkeletonGenerator,
lives in MeshWeaver.Plugin.Build — plugin build tooling, not in the portal compile path.)
g<sha>— fallback for manifest-less CI processes (test hosts), andretained everywhere as DIAGNOSTIC PROVENANCE (logged at warm-up start:
framework identity {id}, build {g<sha>}).All consumers follow the ONE property exactly as wired in #1687 (store tag,
HasUsableBuild/CompiledFrameworkVersion, bake report, build-claim fingerprint, seeder gate, artifact key);AssemblyCacheGenerations.TagOflearns thes+7-hex tag shape.Build plumbing
Directory.Build.props:_MeshWeaverWriteSurfaceManifest(built-inGetFileHashover@(ReferencePathWithRefAssemblies)filtered toMeshWeaver.*) + publish hookup. 🚨 Themulti-arch container publish's
ProduceReferenceAssembly=falseoptimization is REMOVED — refassemblies are load-bearing now (no ref-asms ⇒ impl hashes ⇒ forked identity ⇒ every bake
declines); the PR fix(ci): kill the multi-arch publish CopyRefAssembly race (MSB3883) #552 race stays killed by the per-leg bin/obj isolation.
mw-plugin-test --print-framework-identity— one-line diagnostic (identity + provenance); theproof scripts drive the REAL resolution path through it.
publish-bake-bundles.shskips-with-notice whenprebuilt-bundles/<identity>/<source>/already holds files — an internal-only merge is a no-oppublish ("rebuild only when we need to" applies to the publish too).
notify-dependentspayload drops the (now wrong)identityfield — receivers resolve theirown.
Proof (measured through the real binary,
mw-plugin-test --print-framework-identity)(sha256 of
obj/ref/MeshWeaver.Data.dllacross from-clean rebuilds); internal-member adds flipit for IVT'd assemblies — correct conservatism, documented.
s9f38b6456177317dd2c6389469811f6b;(
s5cfe9fff…==s5cfe9fff…, and reproducible across independent runs);s6adbb981…);CHANGED (
se1518a33…→sfbfd8b4d…) — the emitter full-MVID rule, isolated;s9f38b645…restored — after merging feat(cd): batched publishes — CD_BATCH_WINDOW_MINUTES defers in-window merges to the reconciler #1691/fix(cd): batching probe fails open under every failure shape #1694/fix(content): evaluate the ingest trigger-order claim atomically with the stream apply (#1692) #1695 mid-proof:three real merged commits (CD workflow + ContentCollections internals) moved the identity
NOT AT ALL, live evidence that routine merges now cause zero rebakes.
emitter rule, absence marker), manifest parsing, canonical-list invariants, a closure pin test
that recomputes the tester's csproj closure and fails naming the drift, the process resolution
chain, tag attributability.
-c Release -warnaserror --no-incrementalgreen; affected test projects green.Ops notes
s<hash>identity (a one-time full bake —the key scheme changed); after that, internal-only merges keep
pending≈0end to end and skipthe publish.
BAKE_PUBLISH_TARGETS+ storage role are already provisioned (see feat(#1660 WS3): commit-deterministic framework identity — the CI bake seeds at boot #1687).🤖 Generated with Claude Code