Skip to content

refactor(llm): make AgentOrchestrator injectable; cover agent-mode response metadata - #604

Merged
ginccc merged 7 commits into
mainfrom
refactor/agent-orchestrator-injectable
Jul 22, 2026
Merged

refactor(llm): make AgentOrchestrator injectable; cover agent-mode response metadata#604
ginccc merged 7 commits into
mainfrom
refactor/agent-orchestrator-injectable

Conversation

@ginccc

@ginccc ginccc commented Jul 22, 2026

Copy link
Copy Markdown
Member

Problem

LlmTask built its own AgentOrchestrator inside a 41-parameter constructor, so tests could only substitute a mock through getDeclaredField("agentOrchestrator") reflection.

The premise that motivated this work — "no existing test exercises the agent-mode branches" — turned out to be false, and the real gap is more interesting. LlmTaskCoverage2Test and LlmTaskResumeModeTest already drove ~20 tests through those branches via that reflection hack. But every stub built its result with the two-arg ExecutionResult convenience constructor, which hardcodes Map.of(). Against an always-empty map, ignoring responseMetadata() is indistinguishable from honouring it.

Branch coverage hid a data-flow gap. That is why agent-mode token usage could be computed and then silently dropped while every branch stayed green.

Changes

  • AgentOrchestrator and ConversationHistoryBuilder@ApplicationScoped; the orchestrator's constructor signature is unchanged, so the 8 AgentOrchestrator*Test classes are untouched.
  • LlmTask's constructor: 41 parameters → 20. 22 existed solely to feed new AgentOrchestrator(...), plus attachmentStore; all 23 left with the orchestrator. agentOrchestrator is now a plain private final constructor argument — no reflection, no field-injection seam, no fallback instance.
  • AgentOrchestrator injects its own attachment services. Previously LlmTask's @PostConstruct pushed them in, which only ran if the lazily-created LlmTask existed — so any other future injector of the newly-injectable bean would have received null attachment services and silently lost the readAttachment tool.
  • responseMetadata is copied rather than aliased at all three agent sites: the published map reaches conversation memory and the {{llmMeta}} namespace, and an immutable Map.of() would have made a later metadata write throw on the agent path in production only.
  • All 10 call sites across 9 test files rewritten, plus the mocks, imports and locals the removal orphaned. Net −147 lines.

Verification

  • CDI wiring verified locally. ./mvnw package -DskipTests completes augmentation and ArC emits AgentOrchestrator_ClientProxy and ConversationHistoryBuilder_ClientProxy — these are the repo's first package-private @ApplicationScoped beans, so proxyability was the one real unknown. An unsatisfied injection point in the 29-dependency constructor would have failed the build, so graph resolution is confirmed too.
  • Full suite: 11700 tests, failure set byte-identical to baseline (302 vs 302, names diffed — equal counts alone can mask a swap). The 302 are pre-existing environmental socket failures.
  • Coverage is mutation-verified, not merely green. Reverting 15b7a08a7 turns 4 of the 6 new tests red. The fix has three separately-revertable call sites (skipCascade, standard, executeResume) and there is a discriminating test per site, so a partial revert cannot slip through — an earlier draft of these tests missed the skipCascade site entirely and stayed green when that line was deleted.
  • The other 2 tests pass with and without the fix by design; each says so at its own assertion rather than only in the changelog.

Known trade-off

Adding the defensive copy cost a discriminator. The empty-metadata test previously asserted map identity, which the copy breaks — pre- and post-fix both publish an empty HashMap, genuinely indistinguishable. Mutation score is 4-of-6 rather than 5-of-6. Deliberate: the latent UnsupportedOperationException was judged worth more than the assertion.

Not addressed (pre-existing)

  • executeTask does not null-guard trace() while executeResume does; AgentOrchestratorTest constructs new ExecutionResult(null, null) as a legal shape, so a null trace would NPE at the !toolTrace.isEmpty() check.
  • The LlmTask* tests use bare openMocks(this) with no MockitoExtension, so strict-stubs never runs and their lenient() calls are decorative.

Summary by CodeRabbit

  • Bug Fixes

    • Improved agent-mode reliability when response traces or metadata are unavailable.
    • Preserved response metadata across standard execution, resumed conversations, and fallback flows.
    • Prevented failures when agent execution returns no result.
  • Documentation

    • Updated the changelog with details on dependency injection improvements, reliability fixes, and validation results.

ginccc added 3 commits July 21, 2026 19:04
…tadata

LlmTask built its own AgentOrchestrator, so a test could only substitute a
mock through getDeclaredField("agentOrchestrator") reflection. Make
AgentOrchestrator — and its ConversationHistoryBuilder dependency —
@ApplicationScoped, and field-inject the orchestrator into LlmTask while
keeping the constructor's instance as the direct-construction fallback, so
every existing call site compiles unchanged and the 41-parameter signature
is untouched (removing the 22 orchestrator-only parameters would leave them
dead rather than merely numerous; that is a separate mechanical change).

Add LlmTaskAgentModeMetadataTest covering responseMetadata surfacing on the
live agent branch, the legacy fallback when the orchestrator declines, and
the HITL resume continuation. Contrary to the note in the previous entry,
the agent branches were already covered — every stub just built its result
with the two-arg ExecutionResult convenience constructor, which hardcodes
an empty map, so ignoring responseMetadata() looked identical to honouring
it. Reverting 15b7a08 turns exactly three of the five new tests red.

Drop the three reflection hacks now that the field is assignable.

CDI wiring is not verifiable locally (quarkus:build augmentation needs a
loopback socket); these are the repo's first package-private
@ApplicationScoped beans, so CI is the gate for proxy generation.
Follow-up to 061e894, applying what a two-reviewer pass surfaced.

LlmTask's constructor drops from 41 parameters to 20: the 22 that existed
only to feed `new AgentOrchestrator(...)` leave with the orchestrator, as
does attachmentStore, while AgentOrchestrator and ConversationHistoryBuilder
come in. The agentOrchestrator field returns to `private final` — no field
injection, no fallback instance, no reflection. All 10 call sites across 9
test files are rewritten, along with the mocks, imports and locals the
removal orphaned. Net -147 lines.

AgentOrchestrator now injects its own attachment services instead of having
them pushed in by LlmTask's @PostConstruct. That wiring only ran when the
lazily-created LlmTask happened to exist, so any other future injector of
the newly-injectable bean would have received null attachment services and
silently lost the readAttachment tool. setAttachmentServices survives only
as a seam for directly-constructed orchestrators.

responseMetadata is copied rather than aliased at all three agent sites. The
published map reaches conversation memory and the {{llmMeta}} namespace, and
ExecutionResult's two-arg constructor yields an immutable Map.of(), so a
later metadata write would have thrown on the agent path in production only.

Tests: a sixth case covers the skipCascade agent branch. The fix has three
separately-revertable assignments and the original five reached only two of
them — deleting the skipCascade line left all five green. Reverting only
that line now fails only that test. Fixtures set enableBuiltInTools so
isAgentMode() is true, instead of stubbing a non-null agent result onto a
config for which the real orchestrator always returns null.

Mutation score is 4 of 6, down from 5: the defensive copy costs the
empty-metadata test its identity assertion, since pre- and post-fix both
publish an empty HashMap. The javadoc and changelog say so at the assertion
rather than leaving the stronger claim standing. A false dependency-cycle
justification in the changelog is retracted in place.

Full suite: 11700 tests, failure set byte-identical to baseline (302/302,
no swaps). CDI wiring still needs CI — augmentation cannot run locally.
Corrects a claim I made repeatedly in this session and left standing in
84d2c67's message: that CDI wiring "cannot be validated locally" because
augmentation needs a loopback socket. It does not. `./mvnw package
-DskipTests` runs Quarkus augmentation in ~13s, binding no ports and
touching no database, and ArC emits AgentOrchestrator_ClientProxy and
ConversationHistoryBuilder_ClientProxy into generated-bytecode.jar.

So the one open risk on this branch — whether ArC proxies package-private
@ApplicationScoped beans, a first for this repo — is closed locally rather
than deferred to CI. Resolution of the orchestrator's 29-dependency
constructor is confirmed by the same run, since an unsatisfied injection
point fails augmentation outright.

The error was over-generalizing a real limitation: tests that bind a
loopback socket (*IT.java, HTTP-server tests) genuinely cannot run here.
That says nothing about quarkus:build, and I asserted the extension three
times without testing it.
Copilot AI review requested due to automatic review settings July 22, 2026 09:47
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ginccc, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 9 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f9b0cc3c-12aa-4de9-bac5-3a445120f8b8

📥 Commits

Reviewing files that changed from the base of the PR and between be7ec16 and 24e88a7.

📒 Files selected for processing (11)
  • docs/changelog.md
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskAgentModeMetadataTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskBranchTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskConfigureTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskCoverage2Test.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskCoverageTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskDeepBranchTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskExtendedBranchTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskExtendedTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskResumeModeTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskTest.java
📝 Walkthrough

Walkthrough

The refactor makes AgentOrchestrator and ConversationHistoryBuilder CDI-managed, injects them into LlmTask, removes reflection-based test wiring, and hardens agent and resume metadata handling with null guards and defensive copies. New tests cover metadata propagation and fallback paths.

Changes

Agent orchestration and metadata flow

Layer / File(s) Summary
CDI-managed orchestration wiring
src/main/java/ai/labs/eddi/modules/llm/impl/AgentOrchestrator.java, src/main/java/ai/labs/eddi/modules/llm/impl/ConversationHistoryBuilder.java, src/main/java/ai/labs/eddi/modules/llm/impl/LlmTask.java
Agent orchestration and history building are injected as CDI collaborators, with attachment services injected directly into AgentOrchestrator.
Agent execution metadata hardening
src/main/java/ai/labs/eddi/modules/llm/impl/LlmTask.java
Agent and resume paths null-guard traces and copy response metadata into mutable maps.
Agent metadata behavior coverage
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskAgentModeMetadataTest.java
Tests cover standard and skip-cascade execution, empty metadata, null traces, legacy fallback, and HITL resume results.
Constructor test wiring and documentation
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTask*Test.java, docs/changelog.md
Existing tests use the revised constructor without reflection or removed tool dependencies, and the changelog documents the refactor and verification notes.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LlmTask
  participant AgentOrchestrator
  participant ConversationMemory
  participant TemplateData
  LlmTask->>AgentOrchestrator: execute agent mode
  AgentOrchestrator-->>LlmTask: return trace and responseMetadata
  LlmTask->>ConversationMemory: publish copied metadata
  LlmTask->>TemplateData: publish configured metadata
Loading

Possibly related PRs

  • labsai/EDDI#587: Overlaps in AgentOrchestrator attachment wiring and LlmTask constructor/testing changes.
  • labsai/EDDI#593: Modifies LlmTask response metadata handling in a related execution path.
  • labsai/EDDI#603: Modifies agent-mode and resume metadata propagation in LlmTask.

Suggested reviewers: rolandpickl, copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main refactor and the added agent-mode metadata coverage.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/agent-orchestrator-injectable

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the LLM module to make AgentOrchestrator a CDI-injectable collaborator (removing reflection-based seams in tests) and adds targeted tests that exercise agent-mode response metadata (not just branch coverage). It also includes several related backend hardening changes around quotas, orphan purge safety, and Jackson REST vs persistence serialization.

Changes:

  • Refactor LlmTask to inject AgentOrchestrator + ConversationHistoryBuilder, reduce constructor parameters, and correctly surface/copy agent-mode responseMetadata (including resume).
  • Split REST vs persistence Jackson behavior via @PersistenceMapper (REST Instant as ISO-8601; persistence remains numeric) and pin YearMonth REST shape for UsageSnapshot.costMonth.
  • Tighten operational safety/consistency: tenant quota boundary comparisons (>=), agent-capacity quota enforcement on deploy, orphan purge refusal on incomplete reference scans, and consistent 429 surfacing for async quota denials.

Reviewed changes

Copilot reviewed 44 out of 44 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/main/java/ai/labs/eddi/modules/llm/impl/LlmTask.java Injects orchestrator/history builder; surfaces agent-mode metadata (incl. resume) and removes reflection seams.
src/main/java/ai/labs/eddi/modules/llm/impl/AgentOrchestrator.java Makes orchestrator CDI-managed; moves attachment wiring into CDI field injection.
src/main/java/ai/labs/eddi/modules/llm/impl/ConversationHistoryBuilder.java Marks history builder @ApplicationScoped so it can be injected.
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskAgentModeMetadataTest.java New tests proving agent-mode metadata (token usage) reaches memory + template namespace across branches and resume.
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskTest.java Updates direct-construction test wiring for the new LlmTask constructor signature.
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskResumeModeTest.java Removes reflection injection; passes orchestrator mock via constructor.
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskExtendedTest.java Updates constructor wiring due to orchestrator injection refactor.
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskExtendedBranchTest.java Updates constructor wiring; removes now-unneeded tool/tenant mocks in construction.
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskDeepBranchTest.java Updates constructor wiring; removes now-unneeded tool/tenant mocks in construction.
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskCoverageTest.java Removes reflection seam; steers agent branches through injected orchestrator mock.
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskCoverage2Test.java Removes reflection seam; steers agent branches through injected orchestrator mock.
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskConfigureTest.java Updates constructor wiring after refactor.
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskBranchTest.java Updates constructor wiring after refactor.
src/main/java/ai/labs/eddi/datastore/serialization/SerializationCustomizer.java Applies REST-only Instant ISO override via mapper configOverride.
src/main/java/ai/labs/eddi/datastore/serialization/PersistenceMapper.java New qualifier to select persistence ObjectMapper.
src/main/java/ai/labs/eddi/datastore/serialization/PersistenceMapperProducer.java Produces persistence-qualified mapper built from shared configuration recipe.
src/main/java/ai/labs/eddi/datastore/serialization/JsonSerialization.java Switches to injecting the persistence-qualified mapper.
src/test/java/ai/labs/eddi/datastore/serialization/SerializationCustomizerInstantFormatTest.java New tests pinning REST vs persistence Instant serialization and back-compat deserialization.
src/main/java/ai/labs/eddi/engine/tenancy/model/UsageSnapshot.java Pins REST YearMonth as ISO string via @JsonFormat.
src/test/java/ai/labs/eddi/engine/tenancy/UsageSnapshotSerializationTest.java New tests pinning UsageSnapshot.costMonth wire shape and round-trip behavior.
src/main/java/ai/labs/eddi/engine/tenancy/TenantQuotaService.java Adds checkAgentQuota read-only gate and metrics parity with other quota checks.
src/test/java/ai/labs/eddi/engine/tenancy/TenantQuotaServiceTest.java Adds coverage for agent-capacity quota behavior + metrics semantics.
src/main/java/ai/labs/eddi/engine/tenancy/MongoTenantQuotaStore.java Aligns at-limit comparison with service semantics (>=).
src/main/java/ai/labs/eddi/engine/tenancy/PostgresTenantQuotaStore.java Aligns at-limit comparison with service semantics (>=).
src/test/java/ai/labs/eddi/engine/tenancy/MongoTenantQuotaStoreTest.java Adds boundary test for “exactly at limit” behavior.
src/test/java/ai/labs/eddi/engine/tenancy/PostgresTenantQuotaStoreTest.java Adds boundary test for “exactly at limit” behavior.
src/main/java/ai/labs/eddi/engine/internal/RestAgentEngine.java Ensures async say-path quota denials surface as 429 (mirroring mapper).
src/test/java/ai/labs/eddi/engine/internal/RestAgentEngineTest.java Adds test asserting 429 + Retry-After + body for async quota denial.
src/main/java/ai/labs/eddi/engine/internal/RestAgentAdministration.java Enforces maxAgentsPerTenant on deploy and throws quota exception before async execution.
src/test/java/ai/labs/eddi/engine/internal/RestAgentAdministrationTest.java Updates constructor wiring; makes quota gate transparent for existing behavior tests.
src/test/java/ai/labs/eddi/engine/internal/RestAgentAdministrationExtendedTest.java Updates constructor wiring; makes quota gate transparent for existing behavior tests.
src/test/java/ai/labs/eddi/engine/internal/RestAgentAdministrationQuotaTest.java New tests pinning agent-capacity gate behavior (deny, counting, fail-open, loophole coverage).
src/main/java/ai/labs/eddi/engine/api/IRestAgentAdministration.java Documents deploy endpoint quota behavior and adds 429 response documentation.
src/main/java/ai/labs/eddi/engine/setup/AgentSetupService.java Surfaces quota-denial reasons when calling CDI admin bean directly.
src/main/java/ai/labs/eddi/engine/mcp/McpAdminTools.java Surfaces quota-denial reasons to MCP clients to prevent retry loops.
src/main/java/ai/labs/eddi/datastore/DescriptorStore.java Fixes includeDeleted semantics to be inclusion-based (true = no deleted constraint).
src/test/java/ai/labs/eddi/datastore/DescriptorStoreTest.java Adds tests asserting correct includeDeleted filter construction.
src/main/java/ai/labs/eddi/configs/admin/rest/RestOrphanAdmin.java Adds scan completeness tracking, purge refusal (409) on incomplete reference scan, and fixes descriptor paging semantics.
src/main/java/ai/labs/eddi/configs/admin/IRestOrphanAdmin.java Updates REST docs/defaults: includeDeleted default false for purge; documents 409 on incomplete scan.
src/test/java/ai/labs/eddi/configs/admin/rest/RestOrphanAdminSafetyTest.java New tests pinning paging correctness and purge safety on incomplete scans + includeDeleted forwarding.
src/test/java/ai/labs/eddi/configs/admin/rest/RestOrphanAdminBranchTest.java Updates display names to match renamed scan method naming.
docs/deployment-management-of-agents.md Documents updated orphan scan/purge semantics, defaults, and 409 refusal behavior.
docs/superpowers/specs/2026-07-21-manager-coverage-backend-design.md Adds design/verification notes for manager-coverage-related backend patches.
docs/changelog.md Records the refactor, rationale, and verification notes for the changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/main/java/ai/labs/eddi/modules/llm/impl/LlmTask.java Outdated
Comment thread src/main/java/ai/labs/eddi/modules/llm/impl/LlmTask.java Outdated
executeTask assigned agentResult.trace() straight into toolTrace while
executeResume null-guarded it, so the later `!toolTrace.isEmpty()` check
could throw. Both agent branches now guard, matching the resume path.

Flagged by all three reviewers and previously deferred here as
"pre-existing" — a thin defence when this branch already edits those exact
lines.

Scope, stated precisely because the review overstated it: no production
path returns a null trace. Both ExecutionResult construction sites pass a
fresh ArrayList, and executeIfToolsEnabled returns null rather than a
partial result. This is hardening plus consistency, not a live bug fix.
The shape is legal though — AgentOrchestratorTest constructs
`new ExecutionResult(null, null)` — so the guard has a real contract to
defend.

Mutation-verified: removing it yields `NullPointerException: Cannot invoke
"java.util.List.isEmpty()" because "toolTrace" is null`, failing exactly
the new agentMode_nullTrace_doesNotThrow and nothing else.

Full suite: 11701 tests, failure set byte-identical to baseline (302/302).

Not applied from the same review: the PersistenceMapperProducer finding
(rated High) is incorrect — WRITE_DATES_AS_TIMESTAMPS already defaults to
true and a hand-built ObjectMapper ignores quarkus.jackson config, so the
suggested change is a no-op. Verified by pointing the test at the real
producer: 7/7 green. The genuine defect it brushed against is in that test,
not production, and belongs to the branch that owns the file; recorded in
the changelog.
Copilot AI review requested due to automatic review settings July 22, 2026 14:09
…trator-injectable

# Conflicts:
#	docs/changelog.md

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 44 out of 44 changed files in this pull request and generated 2 comments.

Copilot AI review requested due to automatic review settings July 22, 2026 14:23
@github-actions

Copy link
Copy Markdown

⚠️ Deprecation Warning: The deny-licenses option is deprecated for possible removal in the next major release. For more information, see issue 997.

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@ginccc
ginccc marked this pull request as ready for review July 22, 2026 14:24
@ginccc
ginccc requested a review from rolandpickl as a code owner July 22, 2026 14:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Comment thread src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskAgentModeMetadataTest.java Outdated
Comment thread docs/changelog.md Outdated
Copilot flagged that the javadoc and changelog did not add up: "four" red
plus "the other two" guards cannot describe seven tests. The inconsistency
was real; both the number and the proposed repair were wrong, in opposite
directions.

The review suggested naming resumeMode_nullResult_publishesEmptyMetadata as
the third guard. That test discriminates: pre-fix, executeResume published
no metadata at all, so its verify() fails outright rather than observing an
empty map. Re-measuring by actually reverting the fix gives FIVE of seven
red; the only survivors are agentMode_emptyMetadata_publishesEmptyMap and
agentReturnsNull_fallsBackToLegacyChatExecutor.

Root cause of the stale figure: the count was updated by hand across two
changes that moved it in opposite directions. The defensive copy demoted
agentMode_emptyMetadata_publishesEmptyMap from discriminator to guard
(identity assertion became assertNotSame), and the later null-trace test
added a discriminator back — it asserts tokenUsage is surfaced, not merely
that nothing throws. Netting those by inspection produced a number that had
never been observed.

Both documents now carry the measured figure, and the javadoc tells the next
reader to re-measure rather than adjust the count by hand.
Copilot AI review requested due to automatic review settings July 22, 2026 15:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment thread src/main/java/ai/labs/eddi/modules/llm/impl/AgentOrchestrator.java

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskBranchTest.java (1)

108-108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use a top-level SimpleMeterRegistry import.

Add import io.micrometer.core.instrument.simple.SimpleMeterRegistry; and replace the inline qualified references; no same-name ambiguity is shown.

  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskBranchTest.java#L108-L108: use new SimpleMeterRegistry().
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskConfigureTest.java#L107-L107: use new SimpleMeterRegistry().
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskCoverage2Test.java#L134-L134: use new SimpleMeterRegistry().
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskCoverageTest.java#L122-L122: use new SimpleMeterRegistry().
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskDeepBranchTest.java#L122-L122: use new SimpleMeterRegistry().
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskDeepBranchTest.java#L498-L498: use new SimpleMeterRegistry().
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskExtendedBranchTest.java#L114-L114: use new SimpleMeterRegistry().
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskExtendedBranchTest.java#L479-L479: use new SimpleMeterRegistry().

As per coding guidelines, “Reference types and annotations through top-level imports; do not use inline fully qualified names except to disambiguate same-named types.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskBranchTest.java` at line
108, Replace the inline fully qualified SimpleMeterRegistry references with a
top-level import and the short type name. Apply this in
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskBranchTest.java:108,
LlmTaskConfigureTest.java:107, LlmTaskCoverage2Test.java:134,
LlmTaskCoverageTest.java:122, LlmTaskDeepBranchTest.java:122 and 498, and
LlmTaskExtendedBranchTest.java:114 and 479; no same-name ambiguity requires
qualification.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskAgentModeMetadataTest.java`:
- Around line 249-255: Extend the assertion for stored metadata in
LlmTaskAgentModeMetadataTest so it performs a put operation on stored and
verifies the map is writable, while retaining the existing assertNotSame check
against emptyMetaResult.responseMetadata(). Use the put to confirm the published
defensive copy supports later metadata writes.

---

Nitpick comments:
In `@src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskBranchTest.java`:
- Line 108: Replace the inline fully qualified SimpleMeterRegistry references
with a top-level import and the short type name. Apply this in
src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskBranchTest.java:108,
LlmTaskConfigureTest.java:107, LlmTaskCoverage2Test.java:134,
LlmTaskCoverageTest.java:122, LlmTaskDeepBranchTest.java:122 and 498, and
LlmTaskExtendedBranchTest.java:114 and 479; no same-name ambiguity requires
qualification.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 91bc0b59-2f6e-47ef-80c1-e637d4b8174c

📥 Commits

Reviewing files that changed from the base of the PR and between 1014699 and be7ec16.

📒 Files selected for processing (14)
  • docs/changelog.md
  • src/main/java/ai/labs/eddi/modules/llm/impl/AgentOrchestrator.java
  • src/main/java/ai/labs/eddi/modules/llm/impl/ConversationHistoryBuilder.java
  • src/main/java/ai/labs/eddi/modules/llm/impl/LlmTask.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskAgentModeMetadataTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskBranchTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskConfigureTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskCoverage2Test.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskCoverageTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskDeepBranchTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskExtendedBranchTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskExtendedTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskResumeModeTest.java
  • src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskTest.java

…ry FQNs

Two review findings applied, one declined.

Applied — the empty-metadata guard asserted assertNotSame, a property
Map.copyOf also satisfies while still throwing the moment downstream code
adds a metadata key. That is precisely the failure the defensive copy
exists to prevent, so the assertion was pinning the wrong thing. It now
probes an actual put.

Applied — 25 inline `new io.micrometer...SimpleMeterRegistry()` across nine
LlmTask*Test classes replaced with a top-level import (AGENTS.md 4.7).
Pre-existing, but this branch carried them forward when it rewrote the
constructor call sites.

Declined — privatising AgentOrchestrator's two @Inject attachment fields.
src/main has 43 package-private @Inject fields and zero private ones, so
this would be the only exception; GroupConversationService.attachmentStore
is the same shape and is written directly by its test; and Quarkus
recommends package-private here to avoid reflective injection, which the
roadmap's native-image work would pay for. The cited risk of in-package
mutation is unchanged either way, since the package-private
setAttachmentServices setter beside the fields exists so the eight
AgentOrchestrator*Test classes can write them.

Full suite: 11713 tests, failure set byte-identical to baseline (302/302).
Copilot AI review requested due to automatic review settings July 22, 2026 16:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

@ginccc
ginccc requested a review from aisabella-ai July 22, 2026 16:31
@ginccc
ginccc merged commit 2709075 into main Jul 22, 2026
25 checks passed
@ginccc
ginccc deleted the refactor/agent-orchestrator-injectable branch July 22, 2026 16:32
pull Bot pushed a commit to Stars1233/EDDI that referenced this pull request Jul 24, 2026
Brings in PR labsai#604, which made AgentOrchestrator an @ApplicationScoped bean
injected into LlmTask rather than constructed with `new` from 22 pass-through
constructor parameters. That is the same hot pair of files this branch
reworked, so the merge needed real resolution rather than a rubber stamp.

Conflicts (3):
- AgentOrchestrator imports — kept both sides. labsai#604 added @ApplicationScoped
  and @Inject; this branch added ConfigProvider.
- AgentOrchestrator constructor — kept both sides: this branch's
  tokenCounterFactory field and labsai#604's @Inject. CDI supplies the factory
  automatically now that the class is a bean.
- LlmTask — took main's side wholesale; the orchestrator is injected, so the
  `new AgentOrchestrator(...)` call and its 22 parameters are gone.
- docs/changelog.md — both sides prepend; kept both blocks, this branch's
  entries newest-first. Verified every heading present on main survives.

Two semantic breaks git merged silently and cleanly, neither visible to a
`mvn clean compile` of the main sources:

1. LlmTaskAuditLedgerTest (added by this branch) constructed LlmTask with the
   pre-labsai#604 40-argument signature and then reached in via reflection to set
   agentOrchestrator. Both sides edited different regions of the file, so the
   merge succeeded and produced code that cannot compile. Rewritten against
   the 20-argument constructor; the reflection hack is no longer needed.

2. LlmTaskAgentModeMetadataTest (added by labsai#604) stubs executeIfToolsEnabled
   and resumeToolLoop at the arity main had. This branch added a
   JsonResponseFormatPolicy parameter, so the stubs no longer matched, Mockito
   returned null, and LlmTask fell through to the legacy branch — 6 of 7 tests
   died on `ChatResponse.aiMessage() because "messageResponse" is null`.
   Matcher lists widened to the arity production actually calls. This also
   de-vacuums the `verify(never()).executeIfToolsEnabled(...)` at :380, which
   was checking an overload nothing invokes and would have passed regardless.

Also corrects a javadoc claim this branch made that labsai#604 invalidated: the
BUDGET_ENFORCE_DEFAULT comment justified reading through ConfigProvider on the
grounds that AgentOrchestrator "is not a CDI bean". It is one now. The code is
unchanged and still correct — the field is static final, which @ConfigProperty
cannot target either way — but the stated reason was wrong and would have
misled the next reader.

Verification: ./mvnw clean compile, ./mvnw test-compile and ./mvnw validate
all green; 830 tests across 32 classes covering the merge surface, 0 failures.
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