Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
2a31d0e
refactor(groups): extract GroupAttachmentBinder from GroupConversatio…
ginccc Aug 1, 2026
fe2edde
refactor(groups): extract GroupContextBuilder from GroupConversationS…
ginccc Aug 1, 2026
727d516
refactor(groups): extract GroupSigningGuard from GroupConversationSer…
ginccc Aug 1, 2026
8b76d74
fix(groups): key-rotation-safe signature verification
ginccc Aug 1, 2026
741bc3f
refactor(groups): extract MemberTurnExecutor from GroupConversationSe…
ginccc Aug 1, 2026
02d3760
refactor(groups): extract PhaseExecutionEngine from GroupConversation…
ginccc Aug 1, 2026
7de245a
refactor(groups): extract TaskForceEngine from GroupConversationService
ginccc Aug 1, 2026
338ddd4
docs(groups): fix stale PhaseExecutionEngine comment, log review of s…
ginccc Aug 1, 2026
5071acd
refactor(groups): extract GroupHitlCoordinator from GroupConversation…
ginccc Aug 1, 2026
8405935
refactor(groups): extract GroupLifecycleOps from GroupConversationSer…
ginccc Aug 1, 2026
118b310
chore(groups): R1 step 9 verification - facade finalized, no further …
ginccc Aug 1, 2026
2ae7e5d
feat(groups): wire group discussions into graceful shutdown
ginccc Aug 1, 2026
710a562
refactor(orchestrator): extract ToolContextBudget from AgentOrchestrator
ginccc Aug 1, 2026
0809465
feat(orchestrator): introduce the ToolSourceProvider SPI
ginccc Aug 1, 2026
105ce2f
refactor(orchestrator): extract HttpCallToolsProvider, first SPI prov…
ginccc Aug 1, 2026
25dd1b6
refactor(orchestrator): extract McpToolsProvider
ginccc Aug 1, 2026
82fd81b
feat(orchestrator): add A2AToolsProvider, not yet wired
ginccc Aug 1, 2026
76d9e52
refactor(orchestrator): extract DynamicAgentToolsProvider + ToolObjec…
ginccc Aug 2, 2026
a8cc233
refactor(orchestrator): extract ContextualToolsProvider
ginccc Aug 2, 2026
15c0299
fix(review): critical review pass over Wave R — 8 defects, docs corre…
ginccc Aug 2, 2026
b499aa7
fix(review): PR #626 automated-review findings — 6 defects, 2 declined
ginccc Aug 2, 2026
40c7a06
Merge origin/main into refactor/group-service-split
ginccc Aug 2, 2026
bcea36f
refactor(orchestrator): BuiltinToolsProvider + close the three SPI gaps
ginccc Aug 2, 2026
9109084
refactor(orchestrator): buildToolSetup now iterates providers
ginccc Aug 2, 2026
fc80f34
refactor(orchestrator): extract ToolApprovalGateSupport
ginccc Aug 2, 2026
b6e4c6a
refactor(orchestrator): introduce IAgentOrchestrator
ginccc Aug 2, 2026
956aa76
refactor(orchestrator): extract ToolLoopRunner + ToolLoopResumer
ginccc Aug 2, 2026
f0f104c
refactor(conversation): extract ConversationHitlService
ginccc Aug 2, 2026
09c365e
refactor(conversation): extract ConversationStepRunner — Wave R complete
ginccc Aug 2, 2026
135ab46
fix(review): Wave R branch review + PR nitpicks
ginccc Aug 2, 2026
f92fa2c
docs(groups): explain the remaining useless-parameter findings in place
ginccc Aug 2, 2026
7114358
fix(groups): DEBATE opposingArguments team-filter bug (V6(a))
ginccc Aug 3, 2026
f0c3d0c
feat(groups): LiveDiscussionRegistry (Wave 0, F1)
ginccc Aug 3, 2026
58344da
feat(groups): Speaker-level ResumePoint (Wave 0, F2)
ginccc Aug 3, 2026
e9dd3a7
feat(groups): DecisionRecord (Wave 0, F3)
ginccc Aug 3, 2026
74749ca
feat(groups): Transcript entry types + visibility matrix (Wave 0, F4)
ginccc Aug 3, 2026
e805225
feat(groups): GroupCostLedger (Wave 0, F5)
ginccc Aug 3, 2026
4ef2b96
feat(groups): Paused-document schema versioning (Wave 0, F6)
ginccc Aug 3, 2026
3aae80c
feat(groups): Group cost ceiling + attribution (I1)
ginccc Aug 3, 2026
743e9de
docs(orchestrator): correct four Javadocs left stale by R2's rewiring
ginccc Aug 3, 2026
3ada824
fix(orchestrator): UserMemoryTool never resolved its group scope
ginccc Aug 3, 2026
7c0b36e
fix(orchestrator): three SPI hardening fixes from PR review
ginccc Aug 3, 2026
8e2fc2c
feat(groups): Convergence detection + early exit (I2)
ginccc Aug 3, 2026
df2aadf
fix(orchestrator): restore interrupts wrapped in a cause chain
ginccc Aug 3, 2026
cc0fad3
feat(groups): Abstention + minority report (I4)
ginccc Aug 3, 2026
453217b
feat(groups): Structured verdicts + deterministic synthesis (I3)
ginccc Aug 3, 2026
66c7850
fix(groups): wouldExceedCeiling disagreed with enforceCeiling at zero
ginccc Aug 3, 2026
1c00ea8
feat(groups): Agent-writable shared task list (I5)
ginccc Aug 3, 2026
86307be
fix(groups): @Vetoed on GroupTaskTools — it broke application startup
ginccc Aug 3, 2026
03d5fbc
feat(groups): Runtime recruitment + configurable delegation timeout (I7)
ginccc Aug 3, 2026
31d3636
fix(groups): branch-review findings — cross-discussion write, ungated…
ginccc Aug 4, 2026
985c9d2
fix(groups): branch review round 2 — CME on persist, registry leak, c…
ginccc Aug 4, 2026
f7fee59
fix(groups): branch review round 3 — recruits were second-class every…
ginccc Aug 4, 2026
aca5a4a
fix(groups): a continuation round could report the previous round's c…
ginccc Aug 4, 2026
a84647e
test(orchestrator): cover two gates a real regression would have ship…
ginccc Aug 4, 2026
2a3dc6f
fix: the last five branch-review findings
ginccc Aug 4, 2026
8d5235a
fix(groups): pre-merge review — recruitment was inert, three earlier …
ginccc Aug 4, 2026
8306f6d
fix(orchestrator): MCP tool-name collision could run a different serv…
ginccc Aug 4, 2026
e00a940
Merge remote-tracking branch 'origin/main' into refactor/group-servic…
ginccc Aug 4, 2026
344d897
docs(plan): record implementation status and the gaps in what shipped
ginccc Aug 4, 2026
ed156c9
docs(plan): single handoff file so a new session knows what to build …
ginccc Aug 4, 2026
9ba135b
docs(plan): pin the verified anchors for the cost-pricing fix
ginccc Aug 4, 2026
326f0a9
docs(plan): file the round-5 review findings as pre-feature item N3
ginccc Aug 5, 2026
be86f17
Merge remote-tracking branch 'origin/main' into refactor/group-servic…
ginccc Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,67 @@
> **Purpose:** Living document tracking all changes, decisions, and reasoning during implementation. Updated as work progresses for easy reference and review.


---

## 🔍 fix(groups): key-rotation-safe signature verification, plus review-comment cleanup (2026-08-01)

**Repo:** EDDI (`claude/group-collaboration-plan-9bca77`)

Before pushing the three R1 extraction commits, ran a thorough independent review: 5 parallel agents (AGENTS.md compliance, shallow bug scan, git blame/history context, prior-PR-comment context via `gh`, code-comment-vs-code consistency), each reading the diff fresh with no access to the extraction session's own reasoning. Zero regressions from the extraction itself — all three "pure move" claims held up to line-by-line, argument-by-argument scrutiny. Six real findings surfaced; five were doc/comment fixes, one was a genuine pre-existing bug worth fixing in place.

**Real bug fixed — key-rotation-unsafe signature lookups in `GroupSigningGuard`.** Pre-existing on `main` (carried over unchanged by the pure-move extraction; originally flagged by CodeRabbit on PR #494, never fixed). Two call sites resolved a signer's public key via `AgentIdentity.getKeyValidAt(timestamp)` — "whichever key is valid right now" — instead of `getKeyForVersion(exactVersion)`, even though the exact key version used to sign is recorded and available in both cases:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

- **Self-verify at signing time** (`signOutgoingMessage`): could self-discard a just-created, perfectly good signature if the signing key's validity window doesn't yet cover "now" by the time self-verify runs.
- **Peer verify on receipt** (`verifyPriorEntriesIfRequired`): during a rotation overlap window (both old and new key simultaneously valid — the exact scenario `AgentPublicKey`'s own Javadoc says the system is designed to support), every entry was verified against the *newest* valid key regardless of which key actually signed it. Worse, the per-speaker public-key cache was keyed by agent ID alone, so once the first entry from an agent resolved a (possibly wrong) key, every later entry from that same agent — even ones signed with a different key version — silently reused the same cached key without ever consulting its own `signatureKeyVersion`.

Fixed both call sites to use `getKeyForVersion`; changed the verify-side cache key to `agentId#keyVersion` so entries signed with different key versions get independently resolved and cached. Added two regression tests (`GroupSigningGuardTest`) that construct an overlapping-validity two-key identity and assert (via `ArgumentCaptor`) the exact key material passed to `verifyEnvelope` at each call — mutation-checked by temporarily reverting the production fix (`git stash` on just that file) and confirming both new tests fail, and only those two, before restoring it.

**Doc/comment fixes (no behavior change):** an inline fully-qualified name in `GroupSigningGuardTest` (AGENTS.md §4.4 — the only such occurrence across all six new files, everything else in the extraction was already clean); a stale "is now private" comment about `GroupContextBuilder.buildPlainTextFallback`, which is actually `public` (necessarily, for the cross-package delegator call); two Javadoc comments in `GroupConversationService` still naming `lastVerifiedIndex`, a field that moved entirely into `GroupSigningGuard` two commits ago; a `GroupSigningGuardTest` class-Javadoc claim that overstated which characterization suite exercises the signing happy path.

**Two real findings deliberately NOT fixed here** — both pre-existing, both security-relevant, both genuinely delicate rather than mechanical: (1) `verifyPriorEntriesIfRequired` never consults `NonceCacheService` for replay detection, but the sender already calls `validate()` once at signing time (a mutating "mark as seen" op) — naively calling it again on the receive side would make every signature immediately register as "replayed," which is worse than today's gap; needs a non-mutating check method and a decision about what "replay" means on the receive side. (2) `requirePeerVerification=true` is audit-only — a failed verification only logs, the turn proceeds and the receiving agent gets the content anyway; fixing this is a product decision (fail the turn? quarantine the entry? configurable policy?) not a bug fix. Both filed as background follow-up tasks with full context rather than folded into this refactor PR.

Full 15-class group suite green (470 + 12 + 17 + 14, including the 2 new regression tests); clean compile; formatter/Checkstyle clean.

---

## 🧩 refactor(groups): extract GroupSigningGuard from GroupConversationService (2026-08-01)

**Repo:** EDDI (`claude/group-collaboration-plan-9bca77`)

R1 step 3 of `planning/group-collaboration-improvements-plan.md` §3.1: moved the Ed25519 inter-agent signing cluster into a new `ai.labs.eddi.engine.internal.groups.GroupSigningGuard` — `verifyPriorEntriesIfRequired` (receiver-side incremental verification), the signing-creation block that was inline inside `executeAgentTurn` (sign → self-verify → nonce-validate, falling back to unsigned on any failure), and the `lastVerifiedIndex` cursor map that both share.

The signing-creation block previously set four loose local variables (`signature`, `signatureNonce`, `signatureTimestampMs`, `signatureKeyVersion`) that fed straight into a `TranscriptEntry` constructor call; extracted as `signOutgoingMessage(...)` returning a `SigningResult` record (with an `UNSIGNED` singleton for the "not signed, for any reason" case — crypto infra absent, signing not configured, self-verification failed, nonce validation failed), destructured back into the same four constructor args at the call site. `verifyPriorEntriesIfRequired` and the two `lastVerifiedIndex.remove(...)` cleanup call sites (end of a discussion leg; terminal-state cleanup) became one-line delegations.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

Repeated the reflection sweep from step 2 before touching anything: `verifyPriorEntriesIfRequired` is reached via `GroupConversationServiceHitlCoverage3Test`'s reflection helper, so it stays a declared delegator on `GroupConversationService` (same pattern as step 2's seven methods). Added a focused `GroupSigningGuardTest` covering the guard-clause branches directly (12 tests) — the full sign → self-verify → nonce-validate happy path needs real Ed25519 key material and stays covered by the untouched characterization suites instead of being re-mocked here.

Full 12-class group suite + all three new focused test classes: 470 + 12 + 17 + 12, all green; clean compile; formatter/Checkstyle clean. `GroupConversationService` is now 4,186 → 3,977 lines; 3 of R1's 10 extraction steps done.

---

## 🧩 refactor(groups): extract GroupContextBuilder from GroupConversationService (2026-08-01)

**Repo:** EDDI (`claude/group-collaboration-plan-9bca77`)

R1 step 2 of `planning/group-collaboration-improvements-plan.md` §3.1: moved the phase-input-construction and scope-filtering cluster (`buildPhaseInput`, `selectDefaultTemplate`, `filterByScope`, `findLatestResponse`, `mapPhaseToEntryType`, `extractResponse`, `buildPlainTextFallback` — ~280 lines) into a new `ai.labs.eddi.engine.internal.groups.GroupContextBuilder`, constructed once in `GroupConversationService`'s constructor (its only dependency, `templatingEngine`, is never reassigned post-construction, unlike the attachments step's field-injected `attachmentStore`).

**A wrinkle this step surfaced that step 1 didn't:** four characterization test classes (`GroupConversationServiceTest`, `GroupConversationServiceHitlCoverage3Test`, `GroupConversationServiceUncoveredBranchTest`) reach several of these methods via `GroupConversationService.class.getDeclaredMethod(...)` reflection, which requires the method to be *declared directly on that class* — a delegator that's merely inlined at call sites doesn't satisfy it. All seven extracted methods are kept as thin private delegators on `GroupConversationService` for this reason (confirmed by an exhaustive grep for every `getDeclaredMethod("...")` / `method("...")` reflection lookup across the test package before deleting anything — one, `findLatestResponse`, is now reachable only via reflection since its one production call site moved into `GroupContextBuilder` too; left as documented dead-from-production-code, not deleted, since removing it would break the pinned characterization test).
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

Added a new focused `GroupContextBuilderTest` (17 tests, direct construction, no reflection) alongside the untouched characterization suites, per the plan's rule 5. Full 12-class group suite + both new focused test classes: 470 original tests + 12 (step 1) + 17 (step 2), all green; clean compile; formatter/Checkstyle clean.

---

## 🧩 refactor(groups): extract GroupAttachmentBinder from GroupConversationService (2026-08-01)

**Repo:** EDDI (`claude/group-collaboration-plan-9bca77`)

Added `planning/group-collaboration-improvements-plan.md` — the Rev 2.1 implementation plan for group-conversation collaboration features (cost ceilings, convergence detection, voting, negotiation, standing teams, shared artifacts, and more), re-aligned against `main` post-merge of `e20d510a6`. It opens with a **Wave R refactoring workstream**: `GroupConversationService` (4,417 lines), `AgentOrchestrator` (2,725 lines) and `ConversationService` (2,698 lines) are decomposed into focused collaborator classes before any feature work lands, so the ~18 planned items don't pile onto three already-oversized files. Verified the plan's structural claims against the actual repo before starting: line counts, section-banner count (10), and the 12-class/470-test characterization net for `GroupConversationService` all matched exactly.

**This commit is R1 step 1 of that plan** — the first, smallest extraction (the plan's own ordering: static/pure-move clusters first). Moved the attachment-handling cluster (`materializeAttachments`, `rehydrateAttachmentsFromStore`, `grantAndInjectAttachments` — previously ~112 lines inline in `GroupConversationService`) into a new `ai.labs.eddi.engine.internal.groups.GroupAttachmentBinder`, a plain class (not a CDI bean — see the plan's rule 3.0-4: the 12 existing test classes construct `GroupConversationService` directly, and `attachmentStore` is field-injected specifically to keep that compiling, so the extracted collaborator must not force a constructor-signature change). `GroupConversationService` now constructs a `GroupAttachmentBinder(attachmentStore, defaultTenantId)` per call site and delegates — a pure move, no logic changes.

The dedicated `Attachments` nested test class (12 tests) moved from `GroupConversationServiceTest` to a new focused `GroupAttachmentBinderTest`, testing the extracted class directly instead of through the facade. Baselined the full 12-class/470-test `GroupConversationService*Test` suite before touching any code (all green; JaCoCo: 82% instruction / 72% branch on the class) — that is this refactor's regression budget going forward. Re-ran the same suite plus the new test class after the extraction: still all green, 470 tests total (458 in the `GroupConversationService` family + 12 in the new class), `./mvnw clean compile` clean, formatter + Checkstyle clean.

**What's next:** R1 steps 2–10 (extract `GroupContextBuilder`, `GroupSigningGuard`, `MemberTurnExecutor`, `PhaseExecutionEngine`, `TaskForceEngine`, `GroupHitlCoordinator`, `GroupLifecycleOps`, then the graceful-shutdown wiring commit) per `planning/group-collaboration-improvements-plan.md` §3.1, each its own commit. R2 (`AgentOrchestrator` tool-source SPI) and R3 (`ConversationService` HITL split) follow. Full sequencing in the plan's §7 dependency graph.

---

## 🔎 test(configs): sweep the config JSON the ITs build inline, not just the files (2026-08-01)
Expand Down
Loading