fix: MCP tool findings, space-scoped program/quest listings and the budget query path EXO-90210 - #2011
Merged
Conversation
…udget query path EXO-90210 (#2010) Fixes the review findings raised on gamification#2006 (the Gamification MCP tools backport, merged as is by the owner's decision), plus the platform defects they uncovered. MCP tools: - create_campaign drops the open and visibility parameters: a persisted program's "open" IS audienceId == null (ProgramMapper), so both were dead inputs, and an omitted open turned a space-scoped campaign into a platform-wide one visible to everyone. The audience follows space_id, the visibility stays the service's to compute. - list_my_announcements selects announcements through a DAO predicate (RealizationFilter.announcementsOnly -> "creator IS NOT NULL") instead of filtering the page it received; both list tools sort newest first, as their tool definitions already documented. - create_quest takes an optional publish flag and passes a RulePublication, so the stream publication is the caller's decision instead of the lazy back-fill's hidden, empty activity. - get_gamification_leaderboard's period enum says QUARTER, not YEAR. - get_my_campaigns delegates the member/owned union to the new ProgramService.getMyProgramIds, which dedups, excludes soft-deleted programs and paginates once: resolving a deleted program broadcast a program-deleted event on a read path and then failed, shortening the page. - ACL denials are signalled with IllegalAccessException at the 11 sites, per backend-spring.md 5; the LLM-facing messages are unchanged. - A blank description is stored empty: GAMIFICATION_DOMAIN.DESCRIPTION and GAMIFICATION_RULE.DESCRIPTION are NOT NULL, so the documented-optional parameter failed the insert on commit. Space-scoped listings, programs and quests alike. A caller who shares none of the requested spaces is answered with what those spaces show to everyone - their OPEN programs and quests - and with nothing else. Until now the intersection emptied the space list and the audience-free predicate answered a question about one space with every platform-wide item, while an anonymous caller kept the requested audience untouched and was handed the space's RESTRICTED programs. ProgramDAO also left :openVisibility unbound for that shape, which is an HTTP 500 on GET /gamification/programs?spaceId=X for any non-member. The budget-ordered path (sortByBudget) reaches its rows through five Rule.getHighestBudget... named queries rather than ProgramDAO's predicates, so a narrowing expressed only in the predicates did not apply there. Its five branches now mirror them one for one. Two differences remain, deliberately, and are stated in ProgramStorage: that path is enabled-only (it honours neither status nor includeDeleted) and returns only programs that have a rule, so a count taken from the predicates beside such a list can legitimately differ. Sonar: S1172, S1130 and S1128 fixed; S107, S2143 and S6539 accepted, with the reason on the class and in the review log. 379 tests, 0 failures. Every regression pin is mutation-verified, and the hand-written JPQL, the named queries, the NOT NULL constraints and the write refusals are executed by their real engine and services rather than asserted against mocks. (cherry picked from commit 1e7ab04)
|
ahamdi
approved these changes
Sep 21, 2026
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.



Backport onto
developof the validatedfeature/ai-contributionfollow-up to the Gamification MCP tools (Tested & Validated by Samuel on 2026-09-21): the fixes for every finding azayati's review of gamification#2006 raised (that backport shipped as-is by the owner's decision, with EXO-90210 carrying the fixes forward), plus the platform defects those findings uncovered. Onecherry-pick -x, no conflict, no adaptation; all 22 touched files are byte-identical to the FB tip, and none is a new file.1e7ab0415efb65f468What the pick changes
22 files, +1288/−232, all under
services:GamificationMcpTool.java,ai-tool-definitions.json):create_campaigndrops the deadopen/visibilityinputs — a persisted program's "open" isaudienceId == null, and an omittedopenused to turn a space-scoped campaign into a platform-wide, everyone-visible one;list_my_announcementsselects through a DAO predicate (RealizationFilter.announcementsOnly→creator IS NOT NULL) instead of filtering the page it received, and both list tools sort newest first as documented;create_questtakes an optionalpublishflag (aRulePublication) instead of the lazy back-fill's hidden activity; the leaderboard period enum saysQUARTER, notYEAR;get_my_campaignsdelegates to the newProgramService.getMyProgramIds(dedup, excludes soft-deleted, paginates once — resolving a deleted program used to broadcast a delete event on a read path); ACL denials are signalled withIllegalAccessExceptionat all 11 sites (backend-spring.md§5), LLM-facing messages unchanged; a blank description is stored empty (GAMIFICATION_DOMAIN.DESCRIPTION/GAMIFICATION_RULE.DESCRIPTIONareNOT NULL).ProgramDAO,RuleDAO,ProgramFilter,RuleFilter,ProgramServiceImpl,RuleServiceImpl): a caller sharing none of the requested spaces now gets those spaces' OPEN programs/quests and nothing else — before, the emptied intersection answered with every platform-wide item, an anonymous caller was handed RESTRICTED programs, andProgramDAOleft:openVisibilityunbound for that shape (HTTP 500 onGET /gamification/programs?spaceId=Xfor any non-member).RuleEntitynamed queries,ProgramStorage): the fiveRule.getHighestBudget…queries now mirrorProgramDAO's predicates one for one;ProgramStoragestates the two deliberate remaining differences (enabled-only; programs with at least one rule).RuleEntity.java: only two new@NamedQuerydeclarations — no column, table or Liquibase change.RealizationRest.java: 3 lines (sort direction), no new endpoint.Build:
mvn clean installwith tests onefb65f468: BUILD SUCCESS, 381 tests, 0 failures (logbuild-gamif-90210-1.log; the FB PR reported 379 at its own head). NoClaude-Session:URL in the commit.Deployment note: as for the other MCP tool changes of this wave, the AI sees the changed tool definitions after a restart with
meeds.mcp.tools.forceReimport=true(which also resets admin customizations of the AI tools) — hakermi's note on the Tribe task.Classification: N1 proposed, inherited from the socle surfaces the diff carries: ACL signalling changed at 11 sites and the space-scoped visibility predicates rewritten (ACL trigger), a JPA entity file touched (
RuleEntity, named queries only, no schema — still the entity trigger, recall-first), and existing MCP tool definitions changed (create_campaigninput schema,create_questnew input, leaderboard enum — N2 on its own,ai-review-and-merge.md§2). Its approver must be an Architect/Senior Developer who knows it is N1 and is not its author; not to be merged on AI review alone.Knowledge: Meeds-io/eng-standards#81 (
domains/gamification.md§6a the twenty MCP tools and the contract points this delivery fixes, §11 what a space-scoped listing answers to a non-member, §14 four real-engine pitfalls).🤖 Generated with Claude Code