Skip to content

DI PR post-review follow-up 1#5313

Merged
Cervator merged 7 commits intoMovingBlocks:developfrom
SiliconSaga:review/post-di
Apr 2, 2026
Merged

DI PR post-review follow-up 1#5313
Cervator merged 7 commits intoMovingBlocks:developfrom
SiliconSaga:review/post-di

Conversation

@Cervator
Copy link
Copy Markdown
Member

@Cervator Cervator commented Mar 29, 2026

Summary

First pass addressing review findings from #5299 (gestalt-di migration). This PR contains only the review-fix delta — not the full 137-file migration.

This is as much a workflow demonstration as a fix PR. We're using a structured review triage process (GDD framework) to systematically work through findings from BSA, Copilot, and CodeRabbit across the massive DI migration PR, phased to avoid another multi-year review cycle.

Approach

We triaged all review findings into two phases:

  • Phase 1 (this PR): Runtime correctness fixes
  • Phase 2 (future PR): Code quality, pattern enforcement, Context injection cleanup

Review threads on #5299 are intentionally NOT resolved — kept for future reference.

Changes

Finding Result
P1-1: Gestalt version bump Fixed8.0.0-SNAPSHOT8.0.1-SNAPSHOT across settings.gradle.kts and build-logic
P1-2: StateLoading.java (BSA+CodeRabbit) Fixed — loading screen re-pushed after NUI manager swap (fixes flicker); progress bar overflow fixed by recalculating maxProgress when post-context-switch steps are injected
P1-3: PhysicsEngineManager binding (Copilot) False positive — Gestalt auto-maps all interfaces when target==root in bindExpression(). Adding explicit .with(Interface).use(Impl) creates duplicate bean keys and breaks resolution.
P1-4: UniverseSetupScreen wrapper (Copilot+CodeRabbit) FixedsetEnvironment() received a UniverseWrapper but never assigned it to the instance field. One-line fix.
P1-5: AdvancedGameSetupScreen NPE (Copilot) Not reachableNewGameScreen always calls setEnvironment() before showing the screen. Deferred to Phase 2 as defensive hardening.
P1-6: AutoConfigManager check (BSA) Already resolved — BSA reinstated the check before merge.
P1-7: CodeRabbit triage (17 comments) Complete — 5 already resolved, 2 merged into other P1 items, 10 deferred to Phase 2
P1-8: ContextImpl parent BeanContext (CodeRabbit) Fixed — the ContextImpl(Context) constructor created a standalone DefaultBeanContext with no parent, so @Inject resolution in child contexts could not see parent beans. Affects network handlers, NUI manager, and other child-context callers.

ContextImpl fix — test-driven validation

The ContextImpl bug was subtle: context.get() still worked (via fallback to parent.get()), but @Inject resolution through the BeanContext hierarchy was broken. No BeanResolutionException appeared in logs because the exception was caught and silently fell back to the parent Context lookup.

To prove the fix was real, we wrote a unit test (ContextImplTest.childContextResolvesParentBeansViaInject) that:

  • Fails without the fixDependencyResolutionException when injecting a bean registered in the parent
  • Passes with the fix — child BeanContext correctly walks up to the parent

It remains uncertain whether this bug was actively breaking anything at runtime, since context.get() (the common lookup path) worked fine via the fallback. The @Inject path through BeanContext is used by constructor injection during DI container resolution, which would only surface as issues in code paths that rely on the DI container creating objects with injected parent-context dependencies. The network connection handlers (ClientConnectionHandler, ServerConnectionHandler) are the most likely candidates, but we have not yet been able to attribute a specific runtime bug to this fix.

DI Patterns Harvested

While reviewing we discovered and documented several Gestalt DI conventions:

  • Gestalt auto-registers interfaces: .with(Impl.class).lifetime(Singleton) auto-maps all implemented interfaces via interfaceMapping in DefaultBeanContext.bindExpression(). Do NOT add explicit interface bindings — it breaks resolution.
  • Constructor injection preferred, @javax.inject.Inject on protected members as compromise, @In is legacy
  • Never inject Context directly — inject specific dependencies
  • ServiceRegistry for registration phase; ImmutableContextImpl after init — catches rogue writes
  • CoreRegistry is being eliminated — no new uses

Remaining Work

Phase 2 items tracked in our workspace for a follow-up PR:

  • Direct Context injection cleanup (ConsoleImpl, BlockFamilyLibrary, WorldRendererImpl, ReadWriteStorageManager)
  • UniverseSetupScreen — migrate Context.put() to ServiceRegistry
  • Test environment fixes (Environment.java context swap, static context leak, uninitialized Game binding)
  • EntitySystemSetupUtil replay status, EnvironmentSwitchHandler stale type handlers
  • WorldRendererImpl ScreenGrabber visibility (explains black save screenshots)
  • ExternalApiWhitelist sandbox surface hardening

Test Plan

  • Gradle resolves gestalt 8.0.1-SNAPSHOT correctly
  • Single-player game creation works (new game flow, physics, spawning)
  • Universe Setup screen receives seed/settings from Advanced Game Setup
  • Loading screen no longer flickers during context swap
  • Unit test proves ContextImpl parent BeanContext propagation
  • Progress bar stays within bounds during loading
  • Multiplayer connectivity (known issues predate this PR — chest inventory not visible to second client)

Sample Transcript

As an example of human-agent interaction while working this PR see Gist https://gist.github.com/Cervator/4ef1537704e225c60f66c206ba4f0246

Sample Thalamus Snippet

This is a gitignored "midterm memory" storage document in a GDD user's workspace named Thalamus.md for noting down temporary but at times multi-session topics, both from the human and the agent

DI Migration Review

Design spec: docs/plans/2026-03-28-di-migration-review-design.md
PR under review: #5299 | Branch: review/post-di

Phase 1 — Runtime Correctness (current)

  • P1-1: Gestalt version bump to 8.0.1-SNAPSHOT
  • P1-2: StateLoading.java — loading screen orphan fixed, progress bar overflow fixed
  • P1-3: PhysicsEngineManager — FALSE POSITIVE. Gestalt auto-maps interfaces when target==root
  • P1-4: UniverseSetupScreen — wrapper handoff broken (Copilot) — fixed
  • P1-5: AdvancedGameSetupScreen — NPE not reachable (NewGameScreen always calls setEnvironment before showing). Defensive guard deferred to Phase 2
  • P1-6: AutoConfigManager — check already present (BSA reinstated before merge). Null singleton issue (CodeRabbit) deferred to Phase 2
  • P1-7: CodeRabbit triage — 17 comments triaged. 5 already resolved, 1 merged into P1-2, 10 Phase 2, 1 investigated (InitialiseWorld storage path — Phase 2)
  • P1-8: ContextImpl.java:79 — fixed. Parent BeanContext now propagated. Unit test proves the fix (DependencyResolutionException before, passes after)

Phase 2 — Code Quality (parked for later PR)

  • ConsoleImpl: inject deps not Context
  • BlockFamilyLibrary: unnecessary Context injection
  • WorldRendererImpl: replace context param with explicit deps
  • ReadWriteStorageManager: deprecate CoreRegistry-dependent constructor
  • UniverseSetupScreen: migrate Context.put() to ServiceRegistry
  • NUIManagerInternal: rename timedContextForModulesWidgets
  • RegisterRemoteWorldSystems: remove dead commented-out code
  • ModuleManager: document implementation requirement
  • InitialiseBlocks: verify downcast safety
  • InitialiseWorld: clarify where seed is set
  • AutoConfigManager:83: null singleton from failed construction (CodeRabbit)
  • Environment.java:55: context swap in test reset() loses setup hooks (CodeRabbit)
  • HeadlessEnvironment.java:142: BlockFamilyLibrary mismatch with BlockManagerImpl (CodeRabbit)
  • TerasologyTestingEnvironment: static context leaks between tests + uninitialized Game (CodeRabbit)
  • EntitySystemSetupUtil:88: replay status always NOT_ACTIVATED from fresh construction (CodeRabbit)
  • EnvironmentSwitchHandler:105: stale type handlers across env switches (CodeRabbit)
  • ExternalApiWhitelist:62: overly broad engine.persistence.internal whitelist (CodeRabbit)
  • WorldRendererImpl:153: ScreenGrabber only in private context, explains black save screenshots (CodeRabbit)
  • PojoEntityManager:88: Optional deps are bootstrap compromise, tighten later (CodeRabbit)

DI Patterns (living reference — harvest as we go)

  • Constructor injection preferred, @javax.inject.Inject on protected members as compromise, @In is legacy
  • Never inject Context directly — inject specific deps
  • ServiceRegistry for registration phase; ImmutableContextImpl after — catches rogue writes
  • Gestalt auto-registers interfaces: .with(Impl.class).lifetime(Singleton) auto-maps all interfaces via interfaceMapping. Do NOT add explicit .with(Interface.class).use(Impl.class) — it creates duplicate bean keys and breaks resolution. Only use explicit interface mapping when target != impl (e.g., .with(Interface.class).use(() -> specificInstance)).
  • Context.put() should not be called — migrate to ServiceRegistry-based init
  • CoreRegistry is being eliminated — no new uses (test env is reluctant exception)
  • Example: ComponentSerializerTest L59-79
  • Example: TerasologyEngine L260

🤖 Generated with Claude Code

(Or it would have been, but apparently GitHub fine-grained tokens cannot auto-create PRs in a repo it doesn't have write access to? Weak. May have to set @agent-refr as a collaborator. But first: sleep!)

@cursor
Copy link
Copy Markdown

cursor Bot commented Mar 29, 2026

You have used all of your free Bugbot PR reviews.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ffcb221e-7b04-422e-a561-ad8266687550

📥 Commits

Reviewing files that changed from the base of the PR and between 6e3ed3d and 2f16392.

📒 Files selected for processing (7)
  • build-logic/build.gradle.kts
  • build-logic/src/main/kotlin/terasology-module.gradle.kts
  • engine-tests/src/test/java/org/terasology/engine/context/internal/ContextImplTest.java
  • engine/src/main/java/org/terasology/engine/context/internal/ContextImpl.java
  • engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java
  • engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/UniverseSetupScreen.java
  • settings.gradle.kts
✅ Files skipped from review due to trivial changes (2)
  • build-logic/build.gradle.kts
  • engine-tests/src/test/java/org/terasology/engine/context/internal/ContextImplTest.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/UniverseSetupScreen.java
  • build-logic/src/main/kotlin/terasology-module.gradle.kts
  • engine/src/main/java/org/terasology/engine/context/internal/ContextImpl.java

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Fixed loading screen visibility after context switching during startup.
    • Ensured universe setup initializes its environment before preparing the UI context.
    • Improved parent→child context bean resolution so dependency injection behaves correctly.
    • Made loading progress tracked incrementally to reflect per-step progress more accurately.
  • Tests

    • Added tests covering parent–child context bean resolution, injection, and precedence cases.
  • Chores

    • Bumped internal Gestalt library version references used by build tooling.

Walkthrough

Bumped Gestalt snapshot versions; set UniverseSetupScreen.universeWrapper before preparing context; made ContextImpl link child beanContexts to parent beanContexts; added unit tests for ContextImpl parent→child resolution; changed StateLoading to track process costs incrementally and re-push the loading screen after context swap.

Changes

Cohort / File(s) Summary
Gestalt dependency updates
build-logic/build.gradle.kts, build-logic/src/main/kotlin/terasology-module.gradle.kts, settings.gradle.kts
Updated Gestalt version reference from 8.0.0-SNAPSHOT8.0.1-SNAPSHOT (dependency coordinates, annotationProcessor, and version catalog).
Universe setup UI fix
engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/UniverseSetupScreen.java
Now assigns this.universeWrapper = universeWrapper prior to calling prepareContext().
Context bean parent linkage
engine/src/main/java/org/terasology/engine/context/internal/ContextImpl.java
Constructor passes parent's beanContext into DefaultBeanContext when parent is a ContextImpl, enabling parent→child bean resolution for injection and get().
ContextImpl tests added
engine-tests/src/test/java/org/terasology/engine/context/internal/ContextImplTest.java
New JUnit tests covering parent→child resolution via get and inject, override precedence, non-ContextImpl parent behavior, and null-parent handling (includes helper types).
Loading/state flow adjustments
engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java
Introduced addAndTrack(LoadProcess) to increment maxProgress when adding processes; replaced loadProcesses.add(...) calls with addAndTrack(...); after swapping contexts, re-pushes existing loadingScreen to the new NUIManager.

Sequence Diagram(s)

sequenceDiagram
    participant Loader as StateLoading
    participant OldContext as Old Context
    participant NewContext as New Context
    participant NewNUI as New NUIManager
    participant LoadingScreen as LoadingScreen

    Loader->>OldContext: initiate context swap
    OldContext-->>Loader: provide new child Context
    Loader->>NewContext: switch to child context
    Loader->>NewNUI: obtain NUIManager from NewContext
    alt loadingScreen exists
        Loader->>LoadingScreen: re-push to NewNUI
        LoadingScreen->>NewNUI: register/display on NewNUI
    end
    Loader->>Loader: addAndTrack(new LoadProcess)
    Loader->>Loader: maxProgress += process.getExpectedCost()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • BenjaminAmos

Poem

🐇 I nudged a snapshot, sly and spry,
beans now seek parents by and by,
the universe is set in place,
the loading screen hops to a new space,
small hops, big fixes — I nibble with grace.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'DI PR post-review follow-up 1' is vague and does not clearly convey the primary changes (gestalt version bump, ContextImpl fix, StateLoading improvements, UniverseSetupScreen fix). A reader scanning history would struggle to understand the main purpose. Consider using a more descriptive title that captures the key fix, such as 'Fix ContextImpl parent BeanContext propagation and gestalt 8.0.1 updates' or 'Address DI migration review findings: ContextImpl, StateLoading, UniverseSetupScreen'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing Phase 1 runtime correctness fixes, specific findings addressed (P1-1 through P1-8), test validation, and DI patterns discovered. It clearly explains the approach and context for all changes in the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
engine-tests/src/test/java/org/terasology/engine/context/internal/ContextImplTest.java (1)

19-101: Good test coverage for the fix.

The tests comprehensively cover:

  • Bean resolution via get() (fallback path)
  • Bean resolution via inject() (BeanContext hierarchy path - the broken behavior)
  • Child override precedence
  • Null parent handling

Consider adding a test case for when parent is a Context implementation other than ContextImpl (e.g., a mock or ImmutableContextImpl) to document the expected behavior when the parent's BeanContext cannot be extracted. This would serve as regression documentation for the instanceof check behavior.

📋 Optional: Add test for non-ContextImpl parent
`@Test`
public void childContextWithNonContextImplParentFallsBackToGetOnly() {
    // Create a mock/wrapper Context that is not a ContextImpl
    Context nonContextImplParent = new Context() {
        private final Map<Class<?>, Object> services = new java.util.HashMap<>();
        {
            services.put(TestService.class, new TestService("from-non-contextimpl"));
        }
        `@Override`
        public <T> T get(Class<T> type) {
            return type.cast(services.get(type));
        }
        `@Override`
        public <T, U extends T> void put(Class<T> type, U object) {
            services.put(type, object);
        }
    };

    ContextImpl child = new ContextImpl(nonContextImplParent);

    // get() should still work via parent.get() fallback
    TestService viaGet = child.get(TestService.class);
    assertNotNull(viaGet, "child.get() should resolve via parent.get() fallback");
    assertEquals("from-non-contextimpl", viaGet.name);

    // inject() will NOT resolve because parent's BeanContext is null
    TestConsumer consumer = new TestConsumer();
    child.inject(consumer);
    assertNull(consumer.service, "inject() cannot resolve from non-ContextImpl parent's BeanContext");
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@engine-tests/src/test/java/org/terasology/engine/context/internal/ContextImplTest.java`
around lines 19 - 101, Add a test that ensures ContextImpl constructed with a
parent that is not a ContextImpl (e.g., an anonymous Context or
ImmutableContextImpl mock) still resolves beans via child.get(...) fallback but
does not resolve `@Inject` fields via BeanContext; create a non-ContextImpl parent
that implements get/put and registers TestService, construct ContextImpl child
with that parent (use ContextImpl(Context) constructor), assert
child.get(TestService.class) returns the parent's service and that injecting
into TestConsumer leaves consumer.service null, and name the test something like
childContextWithNonContextImplParentFallsBackToGetOnly to document the
instanceof-based behavior.
engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java (1)

394-396: Account only for the processes appended in these steps.

Right now this works because both post-load injectors are queued last, but rescanning loadProcesses bakes that ordering assumption into maxProgress. If another step is ever queued after either injector, its cost gets counted once in init() and again here. Increment maxProgress as each post-switch process is added instead of summing the whole queue.

♻️ Example helper to make the accounting local and order-independent
private void addPostSwitchProcess(LoadProcess process) {
    loadProcesses.add(process);
    maxProgress += process.getExpectedCost();
}

Then route the loadProcesses.add(...) calls in both post-load steps through addPostSwitchProcess(...) and drop the trailing queue scan.

Also applies to: 453-455

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java`
around lines 394 - 396, The code currently recomputes maxProgress by summing
loadProcesses (using loadProcesses and maxProgress in init()), which
double-counts items added earlier and relies on queue ordering; instead, create
a helper (e.g., addPostSwitchProcess(LoadProcess)) that does
loadProcesses.add(process) and immediately increments maxProgress by
process.getExpectedCost(), then replace direct loadProcesses.add(...) calls in
the post-load/post-switch steps with addPostSwitchProcess(...) and remove the
subsequent scan that sums the entire loadProcesses queue (also apply the same
change for the second occurrence that currently recalculates maxProgress).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@engine-tests/src/test/java/org/terasology/engine/context/internal/ContextImplTest.java`:
- Around line 19-101: Add a test that ensures ContextImpl constructed with a
parent that is not a ContextImpl (e.g., an anonymous Context or
ImmutableContextImpl mock) still resolves beans via child.get(...) fallback but
does not resolve `@Inject` fields via BeanContext; create a non-ContextImpl parent
that implements get/put and registers TestService, construct ContextImpl child
with that parent (use ContextImpl(Context) constructor), assert
child.get(TestService.class) returns the parent's service and that injecting
into TestConsumer leaves consumer.service null, and name the test something like
childContextWithNonContextImplParentFallsBackToGetOnly to document the
instanceof-based behavior.

In `@engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java`:
- Around line 394-396: The code currently recomputes maxProgress by summing
loadProcesses (using loadProcesses and maxProgress in init()), which
double-counts items added earlier and relies on queue ordering; instead, create
a helper (e.g., addPostSwitchProcess(LoadProcess)) that does
loadProcesses.add(process) and immediately increments maxProgress by
process.getExpectedCost(), then replace direct loadProcesses.add(...) calls in
the post-load/post-switch steps with addPostSwitchProcess(...) and remove the
subsequent scan that sums the entire loadProcesses queue (also apply the same
change for the second occurrence that currently recalculates maxProgress).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c8f89c45-4524-48a8-ac0e-7ca70e7639f5

📥 Commits

Reviewing files that changed from the base of the PR and between 9a29bf1 and 7ff6f0e.

📒 Files selected for processing (3)
  • engine-tests/src/test/java/org/terasology/engine/context/internal/ContextImplTest.java
  • engine/src/main/java/org/terasology/engine/context/internal/ContextImpl.java
  • engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java

Comment thread engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java Outdated
Comment thread engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java Outdated
Comment thread settings.gradle.kts
// currently not yet for build-logic, see https://github.com/gradle/gradle/issues/15383 , change verisons
// here and there please.
val gestalt = version("gestalt", "8.0.0-SNAPSHOT")
val gestalt = version("gestalt", "8.0.1-SNAPSHOT")
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.

MovingBlocks/gestalt#160 has still not been merged, whereas it should have been by this point. This dependency may well be manually built based on that pull request but we should probably merge it anyway.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yep I took a look, triggered CodeRabbit, and also asked my local Claude. There is a potential issue with an easy fix, although impact is somewhat unknown from me just reading and listening.

Cervator added a commit to SiliconSaga/Terasology that referenced this pull request Mar 31, 2026
Documents the instanceof-based behavior: when the parent is not a
ContextImpl, context.get() resolves via the parent fallback but @Inject
resolution throws DependencyResolutionException since there is no parent
BeanContext to search.

CodeRabbit nitpick on PR MovingBlocks#5313.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
loadProcesses.add(new AddHostPostLoadProcessesStep());
}

private void addAndTrack(LoadProcess process) {
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.

Do we want to use this helper method in initHost() and initClient() as well for consistency?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed!

Cervator and others added 7 commits March 31, 2026 18:51
Aligns version catalog and build-logic with the 8.0.1 gestalt branch
that contains the DI refactor changes this migration depends on.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
setEnvironment() received a UniverseWrapper parameter but never assigned it
to the instance field. The screen's UI bindings read from the field, not the
context, so seed/generator/server settings from AdvancedGameSetupScreen were
silently discarded.

Flagged independently by both Copilot and CodeRabbit on PR MovingBlocks#5299.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The ContextImpl(Context parent) constructor created a standalone
DefaultBeanContext with no parent, so @Inject dependency resolution in
child contexts could not see beans registered in the parent. This
affected network connection handlers (ClientConnectionHandler,
ServerConnectionHandler), NUIManagerInternal, and other callers.

Fixed by extracting the parent's beanContext when the parent is a
ContextImpl, matching the pattern already used in the ServiceRegistry
variant of the constructor.

Includes a unit test that demonstrates the fix: childContext
ResolvesParentBeansViaInject fails with DependencyResolutionException
before this change and passes after.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…overflow

Two related fixes in StateLoading:

1. After SwitchToContextStep replaces the NUI manager with the DI-created
   instance, re-push the loading screen onto the new manager. Previously
   the screen was orphaned on the old manager, causing flicker and
   rendering through a dead manager.

2. Recalculate maxProgress when AddHostPostLoadProcessesStep and
   AddClientPostLoadProcessesStep inject additional load steps. The
   initial maxProgress only counted pre-context-switch steps, so the
   progress bar overflowed once post-switch steps ran.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix progress bar double-counting: extract addAndTrack() helper that
  adds a load process and increments maxProgress in one step, replacing
  the full-queue iteration that re-counted pre-existing steps.
- ContextImplTest: fix copyright year (2026), remove commentary about
  previous behavior per BSA's guidance to describe expected behavior only.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents the instanceof-based behavior: when the parent is not a
ContextImpl, context.get() resolves via the parent fallback but @Inject
resolution throws DependencyResolutionException since there is no parent
BeanContext to search.

CodeRabbit nitpick on PR MovingBlocks#5313.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use addAndTrack() in initHost() and initClient() as well, not just the
post-context-switch steps. This makes maxProgress tracking consistent
across all phases and removes the now-redundant loop in init() that
was double-counting.

BSA review feedback on PR MovingBlocks#5313.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Cervator
Copy link
Copy Markdown
Member Author

I tested the progress bar and noticed it jumps backwards at one point - poked around at some options but found nothing elegant. It jumps due to additional steps being added which makes the current progress percentage shrink dramatically.

Did come up with an idea to just add multiple progress bars if needed to more authentically cover different phases rather than edit the total mid-progress, but .. different PR one day maybe ;-)

@Cervator Cervator merged commit a1d4f8a into MovingBlocks:develop Apr 2, 2026
12 checks passed
@Cervator Cervator deleted the review/post-di branch April 2, 2026 16:37
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.

3 participants