Skip to content

Restore native constructor graphs for decorated originals - #124

Merged
AGiorgetti merged 5 commits into
developfrom
codex/fix-121-decoration-graph-planning
Oct 9, 2026
Merged

AGiorgetti merged 5 commits into
developfrom
codex/fix-121-decoration-graph-planning

Conversation

@AGiorgetti

@AGiorgetti AGiorgetti commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Decorating an implementation-type registration hid its constructor graph. Invalid rejected candidates could resolve successfully, and an invalid selected graph could execute an earlier dependency factory before failing. This regression from 0.7.1 is present in 0.8.0.

Original types now retain native DI registrations under a private reference identity and their original public key. Native DI plans and owns the inner service; factory/map originals keep their existing policy. Scoped originals use a non-disposable holder under a runtime marker so native compiled cache keys retain their identity and disposal happens once. Diagnostics retain their exclusions and singleton context through a small per-provider options service.

The production change is confined to five files, 89 added lines and 8 removed lines. The registration rationale and compiled-cache safeguard are in the architecture document. The guide, skill and changelog are synchronized. ValidateOnBuild can now report decorated original graph errors at startup; differential tests compare the same native validation options. Factory/map/decorator constructors remain opaque to native graph inspection.

Documentation now includes a detailed registration and ownership walkthrough, a resolution diagram, native-constructor failure examples, the scoped compiled-cache rationale, keyed scope isolation, factory/instance ownership, and alternatives to restoring the older design. Decorators must not dispose their injected inner service, through either Dispose() or DisposeAsync(); the usage guide and packaged skill make this rule explicit.

Verification:

  • Rebased onto current develop (bc4e7658b2179945d5794860ec0bcb3fb6b864cc): five linear commits; full file tree is identical to prior PR head 3307571e5d617673701113eab8de8eef9487a6c7. Whitespace checks passed. Rebased-head CI passed for a0165ec66f54f3b88ef65f94370ce07734c1bbbb, including all four test targets and native hot-cache probes; pack/publish steps were skipped.

  • Documentation follow-up: five standalone examples compiled and executed on .NET 10 with DI 10.0.12; local links, code fences, CRLF whitespace and skill validation passed. Source/test tree is unchanged.

  • Documentation-head CI passed for 1e4d926f4cc5119fec953a36075db7cc74a9833d, including all four test targets and native hot-cache probes; pack/publish steps were skipped.

  • Tests added before the fix: 156 failures and 12 passing controls per target on current develop.

  • Full Release suite: 2,918 passed per target on net472/net8.0/net9.0/net10.0; 11,672 total, zero failures or skips.

  • Graph errors require zero dependency-factory/constructor calls. Coverage includes rejected/selected constructors, closed/open generics, enumerable ordering, cycles, key identity, repeated layers, disposal and provider isolation.

  • Native test instrumentation observes compiled accessor replacement for both originals and scoped holders before 100 repeated resolutions and cache-isolation checks. This instrumentation is excluded from the production library.

  • Release solution build with CI settings and skill validation passed. Only the two existing net472 Microsoft.Extensions support warnings remain.

  • Local packed consumers with DI 10.0.12 passed on all four targets: 48 exact issue outcomes, 144 additional graph outcomes, Unkeyed decoration regresses providers without a keyed availability probe #120/DependsOn changes native precedence when FromKeyedServices and ServiceKey share a parameter #122 and 28 original-contract checks.

  • Implementation-head CI passed for 15e25c9f153b8f0ed28070cfe5c64c805f92f7b4, including all four test targets and native hot-cache probes. Pack/publish steps were skipped.

  • Repeated local .NET 8/10 benchmarks with matched DI 10.0.12: warm transient allocations 752 → 216 B/op; new scoped resolution 1,064 → 552 B/op. Provider build/first-resolution medians were 44–72% faster than the release candidate in this fixture; warm transient medians were 71–81% faster. Cached scoped/singleton lookups remain allocation-free. Detailed method and limits are in the discussion.

Fixes #121. Ready for review; no merge or publication.

Co-authored-by: Codex codex@openai.com

Copy link
Copy Markdown
Contributor Author

Implemented in draft PR #124: #124

The original-type activator checked registration availability but did not plan nested dependency graphs. A rejected constructor could hide a missing nested dependency; a selected constructor could run an earlier application factory before the later graph failed. The exact issue probe independently reproduces both behaviors on develop and published 0.8.0 across all supported targets.

The fix validates only original implementation-type descriptors before actual argument resolution. It copies immutable provider registrations in order, restores the original as the final exact binding for its effective key, and uses a temporary native provider to plan an internal generic root. The root's first argument is a guard factory that throws a private marker exception; its second argument requests the original service with the inherited lookup key. Native DI plans the complete graph first. Invalid graphs therefore throw native errors before activation; valid graphs reach the guard, which stops resolution before any application factory or constructor executes.

Successful checks are cached per provider snapshot, original descriptor and effective key. Failures remain uncached. Ordered duplicate descriptors are retained so an invalid earlier enumerable member cannot be hidden by the last binding. Actual resolution continues through the application's provider, retaining its lifetimes, scopes, diagnostic tracking and disposal ownership. Factory originals, earlier decorator factories and nonempty DependsOn maps keep their existing policies.

This uses public DI planning/resolution APIs and adds no private DI member lookup or invocation. Metadata still prefers Mammoth's snapshot and reuses the existing guarded copied-descriptor fallback for plain native providers. Custom probes without metadata retain their availability contract. The cold validation creates a temporary provider; later successful requests use the cache. The guard depends on native DI planning the full graph and resolving constructor arguments in order. Those assumptions and the scope/factory limits are explained in the architecture document, referenced by the code comment and README Architecture section. The vNext changelog is updated.

Tests were committed first: the initial 156 cases produced 144 failures and 12 passing controls per target; the additional 12 enumerable-order/provider-isolation cases all failed against unchanged production code. All 168 new cases now pass.

Verification for final commit 084be8ef9e4898adaba7b4f103da8e99c6f31389:

  • Local Windows restore and Release CI-style build passed.
  • Local and Windows CI full suites each passed 2,746 tests on net472/net8.0/net9.0/net10.0: 10,984 passed, zero failed/skipped per run. net472 executed under .NET Framework 4.8.9345.0.
  • Fixed exact issue probe matches all 12 native/decorated results on each target, with zero application factory calls.
  • Local and CI native DI 10.0.0 cache probes passed on all four targets, each with 100 interleaved enumerations after observed native compilation.
  • Whitespace check passed. No applicable checks were unavailable.
  • Only the two existing net472 support warnings from Microsoft.Extensions.Telemetry.Abstractions and Microsoft.Extensions.Diagnostics.Testing 10.0.0 remain; no compiler/analyzer warnings or errors.
  • Exact-commit CI succeeded. Pack and Publish were skipped.

Paused for review before another issue. #122 remains open and should be resolved and reviewed before completing the release-validation checkpoint. No release actions were taken.

@AGiorgetti

Copy link
Copy Markdown
Contributor Author

Performance review of PR #124 after the concern about complexity and resolution overhead

Compared unchanged PR head 084be8ef9e4898adaba7b4f103da8e99c6f31389 against develop 571df307caf6311b1ec02f63c5134905c3f6b85e on Windows. No tracked source changes were made during this review.

The concern is justified. The successful-validation cache prevents repeated temporary providers, but does not eliminate the ordinary probe, snapshot lookup, weak-table lookup and dictionary lookup on each original transient activation. A cold check copies the entire registration collection, builds a native provider, constructs a synthetic generic root and throws/catches a marker exception. Failed checks are not cached, and concurrent first requests can duplicate the work.

Local probe: a transient original with one transient dependency and one forwarding decorator; plain BuildServiceProvider and Mammoth's factory; 10, 100 and 1,000 additional keyed registrations. Each cold median covers 101 fresh providers with construction and disposal excluded from timing. Each warm median covers nine batches of 100,000 resolutions after 100,000 warmup resolutions. Thread allocations were measured separately. Release builds, DI 10.0.0, DOTNET_TieredCompilation=0. Two complete runs, with reversed variant order in the second run. Every resolution asserted the expected decorator/original graph.

Representative second-run medians, Mammoth factory, 1,000 additional registrations:

Target First resolve develop -> PR Repeated transient resolve develop -> PR First resolve allocation develop -> PR
net472 8.9 -> 110.8 microseconds 1,419.8 -> 1,596.1 ns 3,376 -> 201,272 bytes
net8.0 8.1 -> 73.2 microseconds 403.5 -> 587.8 ns 2,800 -> 199,944 bytes
net9.0 8.5 -> 67.4 microseconds 865.7 -> 1,013.0 ns 2,800 -> 199,520 bytes
net10.0 8.3 -> 66.5 microseconds 625.9 -> 709.5 ns 2,800 -> 199,560 bytes

Repeated-resolution allocations remained unchanged: 1,040 bytes on net472 and 472 bytes on modern targets. Plain-provider first-resolution allocation at 1,000 registrations was approximately 442-444 KB for the PR versus 2.8-3.4 KB for develop, because that path also constructs the immutable snapshot on first use.

These are local Stopwatch/GC microprobes, not BenchmarkDotNet confidence intervals or application-load benchmarks. Timing varies between runs (particularly net8 warm timing); both runs show overhead and the same allocation results. The filler registrations share a service type with distinct keys. Results cover a simple unkeyed transient graph and do not establish costs for every keyed, scoped, singleton, failing, concurrent or deeper graph. Cached scoped/singleton instances normally bypass original activation after the first resolution within their lifetime.

Recommendation: revise before merging. First investigate using native DI's existing per-provider/key caching for successful validation instead of adding several custom cache/metadata lookups to every transient activation. That could reduce both custom cache machinery and warm overhead without new private-DI reflection, but still needs regression and performance verification and does not itself remove the cold registration-copy cost. A direct native-planner reflection bridge would be a separate compatibility choice, requires additional private APIs, and has not been approved or proven as a robust replacement here.

Verification for this review: both performance runs completed on all four supported test targets, with no probe failures. No production changes, commits or pushes were made, so the existing CI result applies to the unchanged PR head; the new performance measurements are local evidence only. Keep #124 in draft and address this review before moving to #122.

@AGiorgetti

Copy link
Copy Markdown
Contributor Author

Issue #121: alternatives without additional service resolutions

Investigated unchanged draft PR #124, head 084be8ef9e4898adaba7b4f103da8e99c6f31389, against develop 571df307caf6311b1ec02f63c5134905c3f6b85e. All code here is an ignored experimental harness. No tracked source, branch, PR implementation or CI commit was changed.

The constraint used here is strict: graph validation must not call GetService/GetRequiredService/GetKeyedService to resolve a synthetic root, guard or validation service. Metadata should reuse the ordinary availability probe that the original activator already acquires. Actual activation still resolves the original constructor's dependencies.

Recommendation

Replace the separate graph-validation pass and repeated constructor selection with a cached constructor and argument-binding plan. Build that plan without activation, then execute it against the actual application scope. Cache by immutable provider identity, original descriptor and effective key. Keep implementation factories and DependsOn policies separate.

The strongest tested route delegates graph planning to native DI in a private registration copy with the original restored as the final exact binding. It calls the internal native planning method directly, extracts the selected constructor and argument call sites, and caches bindings. No temporary-root resolution or throwing guard is needed. Live-provider dependencies and built-ins resolve from the actual application provider; only missing-registration defaults and injected key constants are taken from the plan. No shadow provider or scope factory may escape into actual activation.

Start with cached ConstructorInfo.Invoke and unwrap only its invocation wrapper. This already removes repeated reflection metadata inspection, candidate selection and per-call argument-resolution closures. Expression compilation is optional and was more expensive on the first resolution in these measurements; adding another compilation stage is not necessary for this fix.

This route needs a compatibility decision before implementation. Beyond the existing guarded CallSiteFactory._descriptors field, the prototype uses ServiceProvider.CallSiteFactory, CallSiteFactory.GetCallSite(ServiceDescriptor, CallSiteChain), CallSiteChain construction, ConstructorCallSite.ConstructorInfo, ConstructorCallSite.ParameterCallSites and ServiceCallSite.Value. All would need explicit shape guards and upgrade regression coverage. These internals are used for both provider modes, not just plain BuildServiceProvider. No private cache mutation or native runtime-resolver invocation is proposed.

An independent recursive planner over Mammoth's immutable descriptor snapshot is the alternative if additional private DI access is unacceptable. It can reuse public constructor metadata and the existing activator rules without resolving validation services or building a shadow provider. However, it must reproduce nested constructor selection, exact/open-generic precedence, enumerable order and constraint filtering, built-ins, AnyKey, inherited keys, defaults, cycles and opaque factories. That route adds substantially more code and ongoing native-DI parity responsibility. It was assessed from source; a complete recursive implementation was not built in this investigation.

Other alternatives

Alternative Additional validation resolutions Finding
Separate DI-cached validation singleton Yes Rejected: resolving it adds a lookup to each original activation, even after its successful check is cached.
Native original under a private key No separate validation lookup Rejected as a general fix: private keys change ServiceKey injection and inherited-key lookup.
Native original under the existing private marker type No separate validation lookup Rejected: implementation is not assignable to the marker type; native DI rejects the call site.
Native original under its implementation type Could reuse the existing inner lookup Changes public registration availability; implementation identities can collide across registrations, and an opaque public decorator can hide original self-reference cycles. Not a general compatibility-preserving replacement.
Direct constructor planning against the live native factory No Rejected as a shortcut: the already-cached opaque public factory can hide a self-reference cycle. A counterexample passes direct planning despite the restored native graph correctly rejecting it.
Full public ValidateOnBuild in a shadow collection No Tested, but validates unrelated registrations too. Isolating the root failure requires a tagged descriptor and dependence on native diagnostic formatting. At 1,000 registrations, cold work and allocation grew considerably. Not recommended.
Targeted private ValidateService, then the existing activator No Tested; avoids guard resolution and needs one additional private method. Still builds the shadow provider and repeats original constructor selection at actual activation. It does not solve the repeated planning/allocation problem as effectively as caching the selected constructor.
Cached native constructor and bindings No Best tested candidate; retains native graph planning and resolves only real arguments. Requires more private DI access and provider/key isolation.

The relevant pinned implementation is DI 10 CallSiteFactory and ServiceProvider.

Verification

The alternative executable passed 274 checks on each of Windows net472, net8.0, net9.0 and net10.0: 1,096 checks total, zero final failures. Covered rejected/selected constructors, open/closed generic dependencies, duplicate enumerable registrations, keyed/unkeyed roots, unrelated invalid registrations, cycles, key injection/inheritance, provider/scope-factory identity, DateTime/numeric/enum defaults, registered null values, opaque-factory exception identity, native preferred-constructor behavior, lifetimes and disposal. Both compiled and cached-invocation plans ran the main graph matrix.

Each planning case asserted zero application factory calls. Executing a successful cached root plan through a counting provider performed exactly the constructor dependency lookups, with no probe or validation-service lookup inside that execution. Separate normal and diagnostic Mammoth-provider checks confirmed that the existing ordinary probe's copied descriptors contain the immutable snapshot instance, including pre-instrumentation type registrations. The snapshot can therefore be reused without resolving its support service; this uses the already-approved native descriptor field, not a new private DI member.

The performance harness explicitly retains the activator's existing ordinary-probe lookup. Provider state and snapshots are bound by the experimental Build helper to each constructed provider. Production integration is not completed: it must bind/cache per provider and effective key without sharing plans across different builds of a caller collection. It must also preserve custom-probe behavior, diagnostic context and collectible/provider cleanup. The simple per-provider prototype state is not proposed as a globally captured registration cache.

Earlier harness attempts exposed and repaired fixture expectation, reflection-member visibility, dependency-loading and assembly-reference selection problems. In particular, a bare assembly-name reference selected a nested net10 benchmark DLL for net472; explicit absolute assembly references repaired this. These were harness failures, not production regressions. Final runs completed on all four targets. NuGet vulnerability auditing could not reach api.nuget.org and emitted NU1900; package restoration from the local cache, compilation and execution succeeded. The intentional unused-parameter fixture warnings were suppressed locally; no production warning policy was changed.

The full production test suite and remote CI were not rerun, because production and the PR head were unchanged. This is feasibility evidence, not a validated replacement PR.

Performance evidence

Local Release Stopwatch/GC probes; Microsoft DI 10.0.0; DOTNET_TieredCompilation=0. One transient dependency, one original and one forwarding decorator through Mammoth's factory. Registration counts: 10 and 1,000 additional keyed registrations. Cold median: 101 first resolutions from fresh providers, excluding provider construction/disposal. Warm median: seven batches of 100,000 resolutions after 100,000 warmups and observation that native dynamic resolver compilation completed. No validation/compilation process ran concurrently with the final benchmark run. Results remain exploratory, without BenchmarkDotNet confidence intervals or application-load evidence.

Representative final medians at 1,000 additional registrations:

Target PR warm ns Cached invoke warm ns PR bytes per warm resolve Cached invoke bytes PR first resolve microseconds Cached invoke first resolve microseconds
net472 2,139.1 1,465.2 1,040 448 135.0 116.5
net8.0 526.0 183.7 472 136 75.9 85.7
net9.0 865.9 587.7 472 136 56.9 109.8
net10.0 750.3 320.5 472 136 71.6 66.0

Cached invocation leaves first-resolution allocation around 203-206 KB at 1,000 registrations, comparable to the current shadow-provider approach. It removes the synthetic-root/guard work, but still copies registrations and builds a planning provider. It is not a universal cold-cost improvement.

Compiled plans reduced the .NET 10 warm median further to 196.8 ns, but raised first resolution to 290.2 microseconds. Full public ValidateOnBuild raised .NET 10 cold resolution to 371.1 microseconds and about 786 KB, versus PR 71.6 microseconds and about 200 KB. The cached-invocation route is the more conservative starting point.

These measured gains are optimistic until integrated: production provider/key cache access, synchronization and custom-provider handling may add costs. The benchmark's baseline uses a delegate to the unchanged original NativeConstructorActivator in a factory-original fixture; it is not a separately rebuilt develop package. PR mode uses its current normal implementation-type path. Do not present the prototype results as guaranteed shipped gains.

Run run-probes.ps1 and run-benchmark.ps1 from this folder through rtk. Results are in the target-specific logs. Stop at this issue; #122 and release work remain outside this investigation.

@AGiorgetti

Copy link
Copy Markdown
Contributor Author

On hold at the maintainer's request. Issue #121 stays open and this PR stays draft; preserve its branch, implementation and regression tests.

The issue comment records the decision, DI's opaque-factory boundary, the remaining graph-error/timing differences, the complexity and resolution costs of this fix, and the compatibility tradeoffs of the alternatives. Additional private planning APIs and a permanent factory-semantics contract change are not approved.

Develop and PR head 084be8ef9e4898adaba7b4f103da8e99c6f31389 remain unchanged. Resume only on an explicit maintainer instruction. At the release checkpoint, record #121 as a deferred known limitation and qualify native-DI graph-parity claims.

@AGiorgetti AGiorgetti changed the title Fix decorated original dependency graph planning (#121) Restore native constructor graphs for decorated originals Oct 9, 2026
@AGiorgetti

Copy link
Copy Markdown
Contributor Author

Implemented #121 in draft PR #124, head 15e25c9f153b8f0ed28070cfe5c64c805f92f7b4. The production diff against current develop is five files: 89 added lines, 8 removed. This replaces the held runtime graph checker with native implementation-type registrations. Private type identity retains each original public key and separate registration caches. Scoped originals use a non-disposable runtime-type holder because native IL reconstructs scoped cache keys from type tokens; the native original remains owned once by that scope. Factory originals, caller-owned instances, mapped activation, diagnostics and public lifetimes keep their contracts.

The design and limits explain that ValidateOnBuild can now reject decorated originals at startup. Tests compare identical native validation settings rather than retaining the previous late-error expectation. Native graph inspection remains bounded by factory/map/decorator boundaries.

Verification:

  • Test-first baseline on current develop: 156 failed graph cases and 12 passing controls per target.
  • Local committed source and exact-head CI: 2,918 passed on each of net472/net8.0/net9.0/net10.0, 11,672 total, zero failures/skips. Native-only cache probes also passed on all four targets after observed compilation.
  • New tests observe native accessor replacement for private originals and scoped holders, then repeat 100 resolutions, check independent duplicate-registration caches, ordinary object/implementation registrations, keys, separate scopes and exactly-once disposal. Inspectable cycles and invalid selected/rejected/enumerable graphs fail before application factories execute.
  • Local packed-package consumers pinned to DI 10.0.12 passed on all four targets: 48 exact issue results and 144 broader graph results, all with zero factory calls; Unkeyed decoration regresses providers without a keyed availability probe #120/DependsOn changes native precedence when FromKeyedServices and ServiceKey share a parameter #122 probes; and 28 original-contract checks. Native and decorated issue results match exactly.
  • Release build, whitespace and skill validation passed. Only the two known net472 Microsoft.Extensions support warnings remain. Guide, skill, references and changelog are updated.

Performance uses the same Release harness and DI 10.0.12 for candidate bc4e765, held planner 084be8e, and native fix 0b99baf (identical Git tree to PR 15e25c9). Windows .NET 8.0.31/10.0.12; 64 unrelated keyed registrations; a valid ordinary/open-generic original graph; two forwarding layers. Three independent processes per case, seven warmed measured batches per process. Cold means provider build, scope creation, first resolution and disposal; warm/native compilation is explicitly observed. Allocations are current-thread bytes and exclude background compiler allocations.

Operation Release candidate Native fix Allocation reduction
Warm transient resolution 752 B/op 216 B/op 71%
New scope plus first scoped resolution 1,064 B/op 552 B/op 48%
Cached scoped/singleton resolution 0 B/op 0 B/op unchanged

Across keyed/unkeyed cases on both measured runtimes, provider build/first-resolution medians improved 44–72% versus the release candidate and were also lower than the held planner. Warm transient medians improved 71–81%; new-scope medians improved 77–86%. These are local fixture measurements. Small differences between allocation-free cached lookups are process/tiering noise, not a claimed speedup. No package was published and no PR was merged. The issue remains open for review.

Co-authored-by: Codex codex@openai.com

@AGiorgetti

AGiorgetti commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Documentation reference for this PR: decoration design and ownership.

The document explains the constructor regression with a runnable failure-before-activation example; native implementation-type registrations and private identities; the scoped holder and native compiled-cache limitation; factory layers, supplied instances and keyed scope isolation; diagnostic/validation boundaries; and the tradeoffs of restoring the earlier implementation. It includes a resolution diagram, internal code excerpts and complete consumer programs.

Decorators must not dispose their injected inner service, through either Dispose() or DisposeAsync(). DI disposes container-created originals and decorators independently. A decorator releases only resources it creates and owns; caller-supplied instance registrations remain caller-owned. The usage guide and packaged usage recipe/skill now state this prominently and show correct cleanup.

Verified five documentation examples against the proposal on .NET 10 with DI 10.0.12, including async layer disposal, caller ownership, keys/scopes and graph failures. Link/fence/CRLF checks and skill validation passed. Commit 1e4d926f4cc5119fec953a36075db7cc74a9833d changes four documentation/skill files; production and test sources are unchanged.

CI on the documentation commit passed, including all four test targets and native hot-cache probes. Pack/publish steps were skipped.

Renamed the reference to docs/decorator-architecture.md and titled it "Decorator architecture and design decisions". Repository links and PR references were updated; relative-link and whitespace checks passed.

@AGiorgetti
AGiorgetti marked this pull request as ready for review October 9, 2026 17:48
AGiorgetti and others added 5 commits October 9, 2026 19:51
Preserve keys and native ownership, with a runtime scoped holder for compiled caches. Update differential coverage and consumer guidance.

Co-authored-by: Codex <codex@openai.com>
Document native original activation, private registration identities, scoped
holders, factory layers, validation boundaries, and design tradeoffs with
executable examples. Make inner-service disposal ownership explicit in the
usage guide and packaged skill.

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
@AGiorgetti
AGiorgetti force-pushed the codex/fix-121-decoration-graph-planning branch from 3307571 to a0165ec Compare October 9, 2026 17:54
@AGiorgetti

Copy link
Copy Markdown
Contributor Author

Rebased this PR onto the latest develop (bc4e7658b2179945d5794860ec0bcb3fb6b864cc), with five linear commits: the two regression-test commits, the native-registration fix, the detailed documentation, and the document rename. The superseded graph-validator implementation and duplicate test replay were omitted; the final file tree is byte-identical to previous PR head 3307571e5d617673701113eab8de8eef9487a6c7 (tree b197f22c8fdff578923a1a53ba99d3655f36fb09). No code, tests, or documentation content changed during the rebase.

Published head: a0165ec66f54f3b88ef65f94370ce07734c1bbbb. The explicit force-with-lease checked the previous remote head before replacing history. The architecture/usage references now point to the rewritten head. A recovery bundle is retained locally.

Verification: clean checkout, current develop ancestry, no merge commits in the PR range, exact full-tree equality, and CRLF whitespace checks passed. CI on the rebased head passed, including all four test targets and native DI hot-cache regression probes. Pack/publish steps were skipped. The PR remains ready for review.

@AGiorgetti
AGiorgetti merged commit c6e5fa9 into develop Oct 9, 2026
1 check passed
@AGiorgetti
AGiorgetti deleted the codex/fix-121-decoration-graph-planning branch October 9, 2026 18:30
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.

Decoration skips native dependency graph errors and activates factories before graph failure

1 participant