Skip to content

fix: MCP tool findings, space-scoped program/quest listings and the budget query path EXO-90210 - #2011

Merged
Jihed525 merged 1 commit into
developfrom
backport/EXO-90210
Sep 21, 2026
Merged

Jihed525 merged 1 commit into
developfrom
backport/EXO-90210

Conversation

@Jihed525

Copy link
Copy Markdown
Contributor

Backport onto develop of the validated feature/ai-contribution follow-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. One cherry-pick -x, no conflict, no adaptation; all 22 touched files are byte-identical to the FB tip, and none is a new file.

Task FB PR FB commit Pick
EXO-90210 MCP tools: campaign visibility default, announcement pagination, and the other gamification#2006 review findings #2010 1e7ab0415 efb65f468

What the pick changes

22 files, +1288/−232, all under services:

  • MCP tools (GamificationMcpTool.java, ai-tool-definitions.json): create_campaign drops the dead open/visibility inputs — a persisted program's "open" is audienceId == null, and an omitted open used to turn a space-scoped campaign into a platform-wide, everyone-visible one; list_my_announcements selects through a DAO predicate (RealizationFilter.announcementsOnlycreator IS NOT NULL) instead of filtering the page it received, and both list tools sort newest first as documented; create_quest takes an optional publish flag (a RulePublication) instead of the lazy back-fill's hidden activity; the leaderboard period enum says QUARTER, not YEAR; get_my_campaigns delegates to the new ProgramService.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 with IllegalAccessException at all 11 sites (backend-spring.md §5), LLM-facing messages unchanged; a blank description is stored empty (GAMIFICATION_DOMAIN.DESCRIPTION / GAMIFICATION_RULE.DESCRIPTION are NOT NULL).
  • Space-scoped listings (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, and ProgramDAO left :openVisibility unbound for that shape (HTTP 500 on GET /gamification/programs?spaceId=X for any non-member).
  • Budget-ordered path (RuleEntity named queries, ProgramStorage): the five Rule.getHighestBudget… queries now mirror ProgramDAO's predicates one for one; ProgramStorage states the two deliberate remaining differences (enabled-only; programs with at least one rule).
  • RuleEntity.java: only two new @NamedQuery declarations — no column, table or Liquibase change.
  • RealizationRest.java: 3 lines (sort direction), no new endpoint.
  • Tests: +7 test files / +~900 lines; per the FB PR, every regression pin is mutation-verified and the JPQL, named queries and NOT NULL constraints are executed by their real engine, not asserted against mocks.

Build: mvn clean install with tests on efb65f468: BUILD SUCCESS, 381 tests, 0 failures (log build-gamif-90210-1.log; the FB PR reported 379 at its own head). No Claude-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_campaign input schema, create_quest new 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

…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)
@Jihed525
Jihed525 requested a review from ahamdi September 21, 2026 07:58
@sonarqubecloud

Copy link
Copy Markdown

@Jihed525
Jihed525 merged commit 6825a30 into develop Sep 21, 2026
10 of 11 checks passed
@Jihed525
Jihed525 deleted the backport/EXO-90210 branch September 21, 2026 11:06
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