feat(groups): org/team preset templates (I10) — on the all-features integration branch - #645
Merged
Conversation
…utputPricePer1M (N1) AUDIT_COST summed cascadeCostUsd + toolCostUsd only, so a plain model call contributed $0 — I1's group cost ceiling could never trip for ordinary members and REST served $0.00 as authoritative. Task-level prices (null = unpriced, $0 as before) now price non-cascade calls; the arithmetic lives once in TokenPricing, shared with the cascade path. Cascade turns keep pricing themselves — key-presence discrimination prevents double counting. Negative prices fail at deployment.
…erwriting accumulateNestedGroupCost keyed memberCosts by the GROUP member's agentId, but each turn spawns a fresh child discussion starting at totalCost 0 and the map records by replacement — so only the last child's spend survived. Key by agentId:childConversationId: replacement stays idempotent per conversation and multiple children of one member sum.
…t at creation (N3) A key-less stored document (every pre-F6 document in production) deserialized claiming CURRENT_SCHEMA_VERSION, so prepareForResume's migration ladder ran zero iterations on exactly the documents it exists for. The initialiser is now LEGACY_SCHEMA_VERSION (1) and the single creation point stamps CURRENT — Jackson runs the no-arg constructor either way, so only the creation stamp can distinguish absent from current. Same split applied to ConversationMemorySnapshot, which was correct only by coincidence (CURRENT == floor == 1). Stale Javadoc in both migration registries corrected.
FULL/ANONYMOUS-scope phases re-fed the whole transcript to every member every turn (~quadratic prompt cost). contextWindow config bounds the rendered context: beyond maxRecentEntries the older entries collapse into a rolling summary — extended incrementally at phase boundaries via the shared SummarizationService — or a plain truncation marker when summarization is off, unconfigured, or failing (never blocks). ANONYMOUS keeps its own summary built from Anonymous-labelled input so it can never de-anonymize. Summarizer spend is attributed to the discussion's I1 cost ledger when priced. The stored transcript is never modified.
…nd validators (I17) Members co-edit typed documents (TEXT/MARKDOWN/JSON) through four tools gated by a new artifactConfig, instead of re-parsing each other's prose. Own collection (never embedded - the loop's stale-snapshot persists would clobber it); deterministic version CAS with a re-read-and-merge retry sentence, via a new numeric storeIfFieldEquals overload (Mongo's typed BSON equality never matches a number against a string); declarative validators (JSON Schema/regex/length) hard-checked at config save; writes announced as artifact_updated via a change queue the turn executor drains (tools have no listener reference); close/delete cascade + user-keyed GDPR erasure via a stamped ownerUserId.
…boundary (PR #636 findings) - accumulateCost/recordSystemCost: !(x > 0) + isFinite instead of x <= 0 - NaN fails every comparison, slipped through, and silently disabled every dollar ceiling (NaN comparisons are all false) - the I9 boundary summarizer is now ceiling-gated via wouldExceedCeiling, like the convergence judge and dissent round (mutation-checked) - summaryBoundary counts VISIBLE entries back from the tail, not raw ones, so bookkeeping rows cannot eat the verbatim window - blank llmProvider/llmModel normalize to null in the config choke point - windowed filterByScope copies the live transcript under its monitor - CodeQL: sanitize gc id + group name in the new WARN sites - memberCosts Javadoc rewritten to the actual one-key-one-conversation invariant (judge/dissent keys predate the nested-child keys)
VOTE phases collect explicit ballots; the deliverable is the auditable artifact - weighted tally, raw ballots, losing-side dissents - because LLM ballots are correlated. Independence is enforced at save time (PARALLEL + NONE scope, hard-rejected otherwise), ballots stay peer-hidden via F4's commit-reveal, and parsing is three-tier with out-of-contract votes as non-ballots that count against quorum (as do abstentions). Ties go to the tiePolicy: one moderator tiebreak turn under a separate conversation key, or an honest NONE. HUMAN_DECIDES is save-time rejected until I6 ships. Also folds in the paragraph-4 gap: decision_reached finally has producers (votes and debate verdicts), with a bounded Slack tally block.
A RETRO phase reviews the discussion and distills lessons that persist
as group-visible entries under the synthetic owner "group:<groupId>" -
institutional knowledge that surfaces as {properties.*} in every
member's later discussions and survives any single human's erasure.
- PhaseType.RETRO + RetroConfig(maxLessonsPerRun, maxStoredLessons)
with a built-in template; the engine enforces the configured per-run
cap at parse time regardless of what the model was told.
- RetroEngine: three-tier lesson parse, idempotency key
retro:<sha256(lesson)> with a fixed sourceAgentId so the same lesson
from any speaker dedupes, FIFO eviction past maxStoredLessons.
- Additive team-owner recall branch on BOTH backends (Mongo filter +
Postgres SQL), owners derived from the supplied group ids only; the
user's own scope is untouched. Both shapes pinned by tests.
- retro_recorded event (SSE + listener), null-safe wiring in the
discussion loop before the persist.
AgentGroupConfigurationTest.phaseType_allValues pins the enum size; I14 added VOTE as the 12th value.
AgentGroupConfigurationTest.phaseType_allValues pins the enum size; I8 added RETRO as the 12th value on this branch.
…ontracts - Save-time JSON schema specs now also validate against the bundled 2020-12 meta-schema; parse-only admitted invalid keyword values. - REGEX validators match through a 500ms deadline-guarded CharSequence so a catastrophically backtracking config pattern refuses the write instead of pinning the member turn. - ArtifactConfig tolerates a [null] validator entry so requireValidSpecs can report its position instead of an NPE. - Artifact events: drain+announce serialized on a per-conversation mutex (write order), plus a final announce pass per discussion leg for writes accepted after a timed-out turn drained. - listByGroupConversationId re-sorts oldest-first per its contract; deleteByGroupConversationId gets the processed-set/no-progress guard. - Slack mrkdwn-escapes artifact name/editor id; oversize refusal rounds up; GDPR cascade Javadoc names artifacts; log sanitized.
…sent carry - recordVoteDecision now takes (turnCounter, maxTurns): the moderator tiebreak is a real LLM turn, gated on both the turn budget and the cost ceiling like every other extra call, and counted when it runs. - TallyOutcome carries the parsed ballots so a tie-policy resolution computes losing-side dissents against ITS chosen option; reusing the unresolved record dropped the minority report for the closest votes. - Weighted totals tie by epsilon (1e-9), not ==; ballot weights must be finite at save time (NaN passes every < comparison). - Slack tally lines width-bounded via buildPreview so a synthesis- derived option paragraph cannot push the message past Slack limits. - CodeQL: 6 log-injection sinks sanitized; useless null check removed.
- PostgresUserMemoryStoreUnitTest pinned the pre-I8 bind order; the team-owner recall branch adds derived owner ids and a second group overlap, so the pin now asserts all nine parameters. - CodeQL: sanitize the four RetroEngine log sinks (conversation id, phase name, team owner, exception message).
- Eviction can never delete a team-owned retro lesson: even with the store wall deliberately breached, the sourceAgentId filter holds. - Personal visibility:self entries never cross users through a shared group; the tool only ever queries its own user id.
A MemberType.HUMAN member sits in the roster like any agent, but their
turn pauses the discussion in a new AWAITING_HUMAN_INPUT state until
they submit - deliberately not AWAITING_APPROVAL: approval endpoints
never accept free text, and an inbox must tell "approve/reject" from
"you are up".
- Phase loops intercept HUMAN speakers, render their input exactly like
an agent turn, and commit a pause carrying PendingHumanInput + the F2
ResumePoint (its first producer). The turn is counted at the pause.
- Submission via POST .../human-input + MCP submit_group_human_input;
only the pending member''s own principal (or admin) may submit - an
approver may decide approvals, but speaking as another human is
impersonation. Entry lands as the phase''s natural type; bookmark
advances; CAS makes double-submits a 409; drift-checks refuse before
any mutation.
- humanMemberConfig {turnTimeout, onTimeout=SKIP_TURN|ABORT} rides the
HITL schedule machinery on a new group-human surface; crash recovery
re-arms human-turn timeouts; SKIP_TURN writes a named SKIPPED entry.
- PARALLEL: agents fan out first, humans prompted sequentially against
the pre-fan-out snapshot; a HUMAN_TURN_PARALLEL resume skips the
fan-out instead of duplicating agent turns.
- Save-time matrix: displayName required; no humans in task-force or
targetEachPeer groups; nested groups with humans rejected (+ runtime
backstop); human moderator allowed + warned, and resolveParticipants
no longer demotes them to an agent.
- human_input_requested event (SSE/Slack), HUMAN_TURN inbox entries
with pendingMemberId, availableActions submitHumanInput.
EDDI had win/lose decision forms and no trade form. A negotiation
drafts a compromise with an explicit concession ledger for human
sign-off; the typed structure is the anti-sycophancy mechanism.
- Phase types PROPOSAL + BARGAIN; skipIf=AGREEMENT_REACHED, a single
enum condition (deliberately not an expression language) that skips
the arbitration once a typed AGREEMENT exists.
- NegotiationState on GroupConversation: proposals (OPEN/SUPERSEDED,
acceptedBy, acceptance entry indices) + the concession ledger.
- BARGAIN turns are a typed JSON move with three-tier parsing; an
unreadable turn is prose with no state effect. A concession that
names nothing in return is not recorded. A new proposal supersedes
the mover''s own open one; proposers sign their own terms.
- The open proposals + ledger are appended to every negotiation turn -
the record the outcome will quote.
- Unanimous acceptance ends the bargaining repeats early (the I2
outcome plumbing) and records DecisionRecord{AGREEMENT,
method=negotiation} whose tally.signedAcceptances maps signatories
to their signed acceptance entries - the co-signatures, no new
crypto. Failed bargaining runs the arbitration, whose conclusion
becomes DecisionRecord{VERDICT, method=arbitration}.
- Preset NEGOTIATION: Positions & Interests (parallel, blind) ->
Opening Proposals -> Bargaining (repeats=maxRounds) -> Arbitration
(conditional, own template) -> Synthesis.
- McpToolFilter whitelists submit_group_human_input (CI guard test). - New requireGroupConversationReadAccess: the pending human member may read the status carrying their rendered prompt (REST + MCP, summary now includes pendingMemberId/prompt on both surfaces); the full transcript view stays role-gated. - Mid-phase resume starts at the bookmarked repeat instead of replaying earlier repeats (duplicate turns and spend). - Human-turn resolution: metric/audit/resume-event deferred until the executor submit succeeds; rollback re-checks the control token; the caller gets a freshly-read copy, not the live instance the background leg mutates. - Slack listener releases its completion latch on a human pause; deleting an AWAITING_HUMAN_INPUT conversation runs the paused cleanup; the signing cursor survives a human pause; crash-recovery sweeps isolated; inbox merges both pause states oldest-first. - turnTimeout must be positive; null members list cannot NPE; F2 drift guard scoped to approval bookmarks; CodeQL logs sanitized; HumanTurnRequired @PARAM docs moved to the constructor.
The planner cannot know members'' actual fit or load; the Contract Net Protocol''s announce-bid-award loop maps onto the existing wave scheduler. - assignmentMode = ROLE (default) | BID on TaskDefinition (per task) and GroupTaskConfig (group default), compat ctors; pre-I18 configs resolve to ROLE. - PLAN leaves BID-mode tasks unassigned; each execution wave announces them to eligible members in blind, parallel bid turns - the prompt carries the batch and nothing else (no transcript, no peer bids), and replies land as peer-hidden BID transcript entries. - Deterministic award: highest confidence, ties by speaking order then agent id; a task nobody bid on falls back to ROLE; the auction skips itself (logged) when it cannot beat its own overhead (<2 bidders, <2 tasks, or no turn budget). Bid turns count toward the budget. - The winning bid is per-task metadata (awardedBids on the task list), deliberately not a global DecisionRecord.
…tator # Conflicts: # docs/changelog.md # docs/group-conversations.md # src/main/java/ai/labs/eddi/configs/groups/mongo/AgentGroupStore.java # src/main/java/ai/labs/eddi/integrations/slack/SlackGroupDiscussionListener.java # src/test/java/ai/labs/eddi/configs/groups/mongo/AgentGroupStoreTest.java
A facilitator agent is briefed at configured checkpoints (EACH_PHASE or EACH_REPEAT) with a compact state summary - never the full transcript - and selects one config-enumerated move: CONTINUE, END_PHASE, EXTEND_PHASE (<=2/phase), CALL_VOTE (one-off I14 vote phase, PARALLEL+ NONE by construction), RECRUIT (I7 validation path), ESCALATE_HUMAN (I6 pending-input pause on the configured principal). Every selection is validated, capped (maxMovesPerDiscussion), and recorded: executed moves as peer-hidden FACILITATION entries + audit events + metrics, rejected attempts as FACILITATION entries that never consume budget. Unparseable, disallowed, invalid-in-context or failed -> CONTINUE. CALL_VOTE/EXTEND_PHASE diverge a runtime copy of the phase list, persisted on the conversation (schema v4); every resume surface now resolves effectivePhases() so bookmarks and drift checks compare against the list the pause was taken from. Divergence is one-off: completion and continuation rounds clear it.
CodeQL flagged 6 log-injection sites in the I18 bid round; every caller-influenced value (groupId, task subject, agentIds, exception messages) now passes through LogSanitizer - 7 sites, the 6 flagged plus the bid-turn-failure log.
- Putting new terms on the table now withdraws the mover's signatures from every other open proposal, and signing someone else's terms supersedes the signer's own open offer - a proposal can no longer reach unanimity on a signature its signatory moved away from. A turn carrying both accept and proposal resolves for the proposal (WARN). - CURRENT_SCHEMA_VERSION 3->4: negotiationState is resume-critical; an older pod re-saving a paused v4 doc would drop the table (identity hop, no migration entry needed). - NegotiationState getters return unmodifiable views; mutation goes through addProposal/replaceProposal/addConcession. - Unused phase param dropped from applyRepeat; moderator filter null-safe; 4 CodeQL log-injection sites sanitized.
A group conversation is an episode; the GroupWorkspace persists: a
backlog (SharedTaskList reused whole), cron cadences that pull
executable tasks into task-force discussions, and running team
metrics with per-member reliability recording (no routing on it, v1).
Cadence fires ride the schedule machinery (poller claim/lease/retry;
a dedicated fireTeamCadence branch, the DreamService pattern). The
fire protocol is crash-proof: reconcile the previous run first, pull
top-N executable tasks by priority, claim via conditional store write
(cluster-safe), then run - pulled tasks injected as a runtime copy of
config.tasks via startCadenceDiscussionAsync, with the cadence's
dollar ceiling riding the inherited-ceiling slot and the run
attributed to the cadence creator. Writeback settles at the next fire
or on workspace read: VERIFIED stays and credits the assignee's
stats; anything else returns to PENDING with reviewer feedback
appended - the cross-run retry loop.
REST /groupstore/groups/{id}/workspace (backlog cap 200 actionable,
cron validated at creation) + MCP add_team_task/list_team_backlog.
Permanent group deletion cascades to the workspace.
…sions - Briefing task summary reads one getTasks() snapshot instead of an emptiness check plus a second read (CodeQL TOCTOU, high). - facilitatorExtensions getter returns an unmodifiable view; mutation goes through recordFacilitatorExtension/clearFacilitatorExtensions. - Three caller-influenced log values sanitized.
…backs One slow or backpressured SSE client inside onArtifactUpdated blocked every other turn's end-of-turn drain on artifactAnnounceMutex. Exactly one thread at a time is now the publisher: it drains under the mutex, releases it, fires the callbacks, and loops for late arrivals; other threads hand off and leave. Write order is preserved (single announcer over a FIFO queue) and no caller ever blocks on a listener.
The changelog's test tally is 13 (8 TaskBidEngineTest + 5 TaskForceEngineTest), and the bid-assignment subsection now follows the agent-task caps paragraph so 'Both caps' keeps its referent.
# Conflicts: # docs/changelog.md # docs/group-conversations.md # src/main/java/ai/labs/eddi/configs/groups/model/AgentGroupConfiguration.java # src/main/java/ai/labs/eddi/configs/groups/model/DiscussionStylePresets.java # src/main/java/ai/labs/eddi/configs/groups/model/GroupConversation.java # src/main/java/ai/labs/eddi/engine/internal/GroupConversationService.java # src/main/java/ai/labs/eddi/engine/internal/groups/GroupContextBuilder.java # src/test/java/ai/labs/eddi/configs/groups/model/AgentGroupConfigurationTest.java
# Conflicts: # docs/changelog.md # docs/group-conversations.md
…lates # Conflicts: # docs/changelog.md # docs/group-conversations.md # src/main/java/ai/labs/eddi/configs/groups/model/AgentGroupConfiguration.java # src/main/java/ai/labs/eddi/configs/groups/model/DiscussionStylePresets.java # src/main/java/ai/labs/eddi/engine/api/IGroupConversationService.java # src/main/java/ai/labs/eddi/engine/internal/groups/GroupContextBuilder.java # src/main/java/ai/labs/eddi/engine/lifecycle/GroupConversationEventSink.java # src/test/java/ai/labs/eddi/configs/groups/model/AgentGroupConfigurationTest.java
…pause A human turn pauses mid-repeat, after other speakers appended this repeat's entries; the resumed leg recomputed the slice base from the current transcript size, so the repeat slice covered only post-pause entries - the convergence check (and, once merged with I14, the VOTE tally) silently lost every pre-pause contribution. New persisted pausedRepeatSliceBase, written at the pause commit and consumed exactly once with the speaker-bookmark read-and-clear discipline.
Two findings, one root cause: the EACH_REPEAT checkpoint ran after the last-repeat decision block. END_PHASE broke past it (a VOTE phase it ended never tallied its cast ballots; verdicts/dissents/retro skipped alike), and EXTEND_PHASE at a final repeat re-ran an already-fired block (duplicate dissent rounds, decision_reached twice). The consult now precedes the block: END_PHASE folds into the phase outcome, EXTEND_PHASE defers the block to the true final repeat, and INSERT_VOTE/ESCALATE apply after it.
continueDiscussion cleared every other round-scoped conclusion but not the persisted negotiation state - round 2 ran against round 1's proposals, and one fresh acceptance could reach unanimous agreement on signatures cast for a different question.
…ed cost ceiling, backlog caps, bounded feedback, resume-rollback cancel race Four confirmed findings from the 23-agent cross-branch final review, all on this branch's surface, plus one HITL minor: - settle() released the cadence claim unconditionally, clobbering a claim a concurrent reconciler had already taken; now a conditional release via casRunningDiscussion(settledDiscussionId) — a lost race drops the caller's stale mutations instead of orphaning the newer discussion's outcomes - inheritedCostCeiling was transient, so a cadence run pausing for approval resumed with no cost ceiling; now persisted - REST addBacklogTask + MCP add_team_task bypassed the agent-surface caps: subject 200 / description 4000 enforced, duplicate subjects rejected (writeback matches outcomes by subject), cadences capped at 20/workspace, inputTemplate bounded at 4000 - COMPLETED writeback appended each failed run's whole agent output to the persisted description; appendFeedbackBounded caps 500/run and trims the total from the front to the shared 4000 cap - claim-CAS store failure now cancels the just-started discussion before rethrowing; executor-saturated resume rollback uses remove-and-recheck so a racing cancel converts instead of being dropped (mutation-verified test)
# Conflicts: # docs/changelog.md # src/test/java/ai/labs/eddi/engine/internal/GroupConversationServiceExtendedTest.java
# Conflicts: # docs/changelog.md # src/main/java/ai/labs/eddi/engine/internal/groups/GroupLifecycleOps.java
# Conflicts: # docs/changelog.md # src/main/java/ai/labs/eddi/engine/internal/GroupConversationService.java
… backlog caps, bounded feedback) # Conflicts: # docs/changelog.md # src/main/java/ai/labs/eddi/engine/mcp/McpGroupTools.java # src/test/java/ai/labs/eddi/engine/internal/groups/GroupHitlCoordinatorTest.java
- the RETRO template now quotes the CONFIGURED maxLessonsPerRun (a group set above the default could never obtain its configured lesson count); buildPhaseInput gained a RetroConfig param, all agent-turn sites pass it - maxLessonsPerRun bounds the whole harvest, not each transcript entry — a multi-speaker retro could store cap x entryCount before eviction - the null-memory-store branch fires retro_recorded with zero instead of returning silently - the cross-user scoping fixture now genuinely belongs to user-2
…rsist-time test (review round) - CURRENT_SCHEMA_VERSION 3 -> 4: pausedRepeatSliceBase is resume-consumed persisted state; legacy docs default to -1 via Jackson (no migration) - the approver detail=full window now requires AWAITING_APPROVAL on both REST and MCP surfaces — the shared paused predicate also admitted approvers to the transcript during AWAITING_HUMAN_INPUT - the mid-repeat pause test records pausedRepeatSliceBase at persist time inside the update() stub instead of asserting the mutable instance
…into I13 # Conflicts: # docs/changelog.md
… round 2) - optimistic-concurrency revision stamp on GroupWorkspace with a new casRevision store primitive; both backlog-add surfaces (REST + MCP) run a bounded read-validate-mutate-CAS loop so concurrent adds cannot drop each other or slip past the cap; exhaustion is an honest 409 - readOrCreate converges duplicate inserts (no unique constraint exists in the storage abstraction): racers delete their own duplicate and adopt the deterministic survivor; find() picks the same survivor - addCadence deletes the just-created schedule when the workspace write fails, closing the orphaned-schedule-fires-forever path - permanent group deletion retires cadence schedules before the workspace, in the crash-recoverable order - run-claim disappearance warning sanitizes groupId (CodeQL)
# Conflicts: # docs/changelog.md # src/main/java/ai/labs/eddi/configs/groups/model/GroupConversation.java
# Conflicts: # docs/changelog.md # src/main/java/ai/labs/eddi/engine/internal/groups/GroupContextBuilder.java # src/main/java/ai/labs/eddi/engine/internal/groups/PhaseExecutionEngine.java # src/test/java/ai/labs/eddi/engine/internal/groups/PhaseExecutionEngineTest.java
…review round 3) - RetroConfig clamps maxLessonsPerRun/maxStoredLessons to hard ceilings (20/500) — a config carrying Integer.MAX_VALUE unbounded the LLM write surface the record exists to bound - FIFO eviction sorts by createdAt (setOnInsert at the store) instead of the updatedAt-ordered recall — a reharvested old lesson refreshed its updatedAt and shielded itself while a later-created lesson was evicted
# Conflicts: # docs/changelog.md
…h test pins (review round 3) - casRevision converts a corrupt (non-numeric) persisted revision into the declared ResourceStoreException instead of an uncaught NumberFormatException (CodeQL) - findWorkspaceIds fetches up to 50 ids — a limit of 2 let 3+ racers compute different survivors and never converge - casRunningDiscussion's Javadoc re-attached (casRevision had been inserted between doc and declaration); legacy null-revision one-write window documented on the interface - schedule-retirement test pins the crash-recoverable ORDER via InOrder; backlog rejection tests pin never-casRevision (the real write path); the lost-settle test pins the failed CAS as the only store interaction
aisabella-ai
self-requested a review
August 8, 2026 08:15
aisabella-ai
approved these changes
Aug 8, 2026
…ded metrics, template honesty (pre-merge review) Six confirmed findings from the final pre-merge deep review of the facilitator subsystem (never externally bot-reviewed) and the integration-only content: - CRITICAL: a deferred EACH_REPEAT escalation at a phase's final repeat returned before the phase-boundary HITL gate, silently skipping a requiresApproval phase's mandatory approval; the gate now supersedes the escalation (suppression entry preserves the question) - CALL_VOTE rejected when the phase ended by signal or a DecisionRecord exists — the inserted vote's tally replaced signed AGREEMENTs and debate VERDICTs - rejection metrics tag bounded to enum names + UNKNOWN (raw LLM move strings created unbounded Prometheus label cardinality); rawMove length-capped at parse - negotiation-table manifest no longer promises a nonexistent human-arbiter mode; research-pod sets summarizeOverflow:false explicitly (no summarizer model is named) - human-input 409 body surfaces the config-drift cause instead of misstating the state - three e2e tests for the previously untested deferred EACH_REPEAT branch (mid-phase repeat+1 arithmetic, boundary phase+1, gate wins)
ginccc
added a commit
that referenced
this pull request
Aug 8, 2026
…ion, tool provenance - AGENTS.md phase-8 row said 60+ MCP tools while every other doc now says 80+ (actual 82) - the opt-in-by-absence convention over-claimed: only artifactConfig and taskListConfig assemble tools; contextWindow/facilitator/humanMemberConfig gate behaviour, not tool assembly - #636 carried the pre-feature defects, not one of the nine items — the changelog and planning header now say #637-#645 with #636 named separately - the MCP table notes which group tools come from the HITL tool set, since they are not declared in McpGroupTools
pull Bot
pushed a commit
to Stars1233/EDDI
that referenced
this pull request
Aug 12, 2026
Brings every user-facing doc in line with what shipped in PRs labsai#636-labsai#645. Each claim verified against source, not against the plan. - group-conversations.md: fixed a nonexistent REST endpoint and the stale HUMAN_DECIDES note; completed the REST (+21), MCP (+10), PhaseType, TaskStatus, ProtocolConfig and DynamicAgentConfig tables; new sections for per-phase controls, dissent and the 23 SSE events; documented the RETRO ceilings; fixed the orphaned task-cap paragraph, re-parented bid-based assignment under TASK_FORCE, unglued 8 headings - README: 6 -> 7 styles, ten new capability bullets, MCP 60+ -> 80+, tests 11,000+ -> 14,000+, OpenAI-compatible docs row - AGENTS: 7 styles, new 10c/10d roadmap rows, tests -> 14,000+, HITL row clarified, opt-in-by-absence convention documented in 4.2 - docs/README: 6.0.0 -> 6.2.0, MCP 48+ -> 80+, HITL + Open WebUI entries - SUMMARY: hitl.md and open-webui-integration.md were missing entirely - rag.md: gemini embedding provider and chroma vector store rows - planning: the group-collaboration queue is empty; PRs recorded
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The final queue item from
planning/group-collaboration-improvements-plan.md, shipped last by design so templates only reference features that exist — and, by necessity, the branch that proves everything works together: it merges all seven feature branches (#637 I17, #638 I14, #639 I8, #640 I6, #641 I11, #642 I18, #643 I12, #644 I13) plus the pre-feature defects branch (#636, N1/N2/N3/I9). 3720 targeted tests, zero failures, after real semantic merge resolutions (below).I10 — Preset templates
Enterprises understand "research pod" and "decision board", not
contextScope: OWN_FEEDBACK. Five packaged templates insrc/main/resources/group-templates/(classpath-loaded via theinitial-agents/index pattern) ship complete, validated configs whose member slots are named roles; instantiation assigns agents (or human principals) to them and saves throughRestAgentGroupStore.createGroup— the normal path, so every save-time validation applies. Placeholder substitution ($role→ agentId) is the ONLY templating.research-podeditorial-teamops-task-forcedecision-boardnegotiation-tableSurfaces: REST
GET/POST /groupstore/templates[/{id}[/instantiate]], MCPlist_group_templates+create_group_from_template. Missing/unknown roles fail loudly, naming the template's real roles.Deviation, upheld and documented:
HUMAN_DECIDESvote tie-breaking stays save-time rejected. Wiring it means a pending-ballot pause whose human answer must parse back into the tally — real machinery, not the "small follow-up" the I14 note hoped.decision-boarduses a HUMAN voting member +MODERATOR_DECIDESties instead of shipping a silently-degrading enum value.Integration-merge resolutions (the load-bearing ones)
DiscussionPhaseunified to 13 components (voteConfig12th,skipIf13th) with both 12-arg compat constructors kept, so every branch's call sites compile unchanged.PhaseTypeunions to 15 (VOTE + PROPOSAL + BARGAIN + RETRO); every enum pin updated.runtimePhases, I11negotiationState) under one release-shape version.decision_reachedfired after the dissent round; anarbitratedflag is true only when this call set the decision, so the event never re-announces an earlier phase's.AgentGroupStore.create/updateruns all five validators (HITL, vote, human, facilitator, artifact).Cross-branch defect only this tree could show (fixed here): I9's windowing overload never reached I6's human-turn prompt render — agents got windowed context while a human's rendered prompt used the unwindowed compat path, breaking I6's "the human sees exactly what an agent speaker would" contract. Both human-prompt sites now pass the context window; the I6 blindness tests verify the windowing overload. The same resolution applies when #636 and #640 meet in main.
Tests
+13 for I10: every template instantiates with dummy assignments and passes the entire save-time validator matrix — the plan's designated integration test of all Wave 1–3 config surfaces; placeholder-free rosters; the human director survives as a HUMAN member; the NEGOTIATION preset expands; missing/unknown-role errors name what's wrong; REST instantiation captured through the store path with 400s saving nothing; MCP filter pins green. Full
engine.internal+configs.groups+engine.hitl+engine.mcp+engine.runtime.internalsuites: 3720 green.