Skip to content

feat(api): describe_feature tool (chat + MCP) - #183

Merged
thomasluizon merged 2 commits into
mainfrom
feature/describe-feature
Jun 5, 2026
Merged

feat(api): describe_feature tool (chat + MCP)#183
thomasluizon merged 2 commits into
mainfrom
feature/describe-feature

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

Summary

Adds a read-only describe_feature tool to Orbit AI's dual agent surface (chat IAiTool + MCP [McpServerTool]). The tool returns the pre-authored markdown explanation for one of 8 feature keys plus its metadata (display_name, related_capabilities, related_surfaces), so the assistant can explain app mechanics on demand instead of carrying that prose in the system prompt.

The content bundle was already embedded; this PR wires it into the agent surfaces.

Changes

  • Shared service: new IFeatureExplanationService / FeatureExplanationService in Orbit.Application loads + parses the embedded 8-file FeatureExplanations bundle once (hand-rolled frontmatter parser, no YAML dependency). Registered as a singleton; both surfaces resolve the same instance.
  • Chat tool: DescribeFeatureTool : IAiTool (IsReadOnly = true) with a required feature_key enum mirroring the 8 keys; returns markdown body + metadata payload.
  • MCP tool: FeatureTools.DescribeFeature [McpServerTool] calls the service directly — pure read, not routed through McpExecutorBridge (per the read-tool routing rule). Registered via .WithTools<FeatureTools>().
  • Catalog: new AgentCapabilityIds.DescribeFeature capability (Low risk, AgentConfirmationRequirement.None, catalog domain, reuses the existing catalog_read scope) maps describe_feature in both chatTools and mcpTools.
  • System prompt: one additive pointer (rule 19) in GlobalRulesSection.cs telling the model to call describe_feature for mechanic questions. Net-additive — rules 1-18 unchanged; no trim.
  • Tests: enum↔file three-way agreement (enum == embedded keys == service keys), tool behavior + metadata, and drift tests pinning AppConstants freeze/lookback/paygate/user-fact/overdue constants and the full LevelDefinitions XP table to the markdown. Confirmed the drift test fails when a constant is bumped, then reverted.

Validation

  • dotnet build Orbit.slnx — 0 errors (pre-existing NU1902/MSB3277/CS8602 warnings only).
  • Orbit.Application.Tests — 1718 passed, 0 failed.
  • Orbit.Infrastructure.Tests — 932 passed, 0 failed (incl. AgentCatalogServiceTests mapping guardrails + GlobalRulesSection pointer assertion).
  • Orbit.Domain.Tests — 344 passed, 0 failed.

Refs thomasluizon/orbit-ui-mobile#90

🤖 Generated with Claude Code

Add a read-only describe_feature tool to both agent surfaces so the
assistant can explain Orbit mechanics (streaks, freezes, frequencies,
gamification, notifications, paygate, schedule-math, ai-memory) from the
pre-authored markdown bundle instead of carrying that prose in the system
prompt.

- New shared IFeatureExplanationService loads and parses the already
  embedded 8-file FeatureExplanations bundle (hand-rolled frontmatter
  parse, no YAML dependency); both surfaces resolve the same singleton.
- Chat DescribeFeatureTool : IAiTool (IsReadOnly) returns the markdown
  body plus display_name / related_capabilities / related_surfaces.
- MCP FeatureTools [McpServerTool] calls the service directly (pure read,
  not via McpExecutorBridge).
- New DescribeFeature catalog capability (Low risk, no confirmation,
  reuses catalog_read scope) maps describe_feature in chatTools + mcpTools.
- Additive system-prompt pointer as rule 19 in GlobalRulesSection.
- Tests: enum<->file three-way agreement, tool behavior/metadata, and
  drift tests pinning AppConstants + LevelDefinitions XP table to the
  markdown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

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

Solid addition. The IFeatureExplanationService singleton parses the embedded bundle once at startup and exposes it correctly to both surfaces (chat IAiTool and MCP [McpServerTool]). Frontmatter parser handles CRLF normalization, missing fields, and unterminated fences correctly. Service registration order is right — singleton injected into scoped DescribeFeatureTool is fine. The ClaimsPrincipal user parameter in FeatureTools follows the established MCP injection pattern across the codebase. isPhaseOneReadOnly: false is consistent with the entire existing catalog. Test coverage is thorough: three-way enum/file/service agreement, tool metadata, success/error paths, and the drift tests that pin AppConstants and LevelDefinitions to the markdown prose are a nice safeguard.

# Conflicts:
#	src/Orbit.Api/Extensions/ServiceCollectionExtensions.cs

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

The merge commit only resolves a conflict in ServiceCollectionExtensions.cs — all three DI registrations (AddScoped<IAiTool, DescribeFeatureTool>, AddSingleton<IFeatureExplanationService, FeatureExplanationService>, .WithTools<FeatureTools>()) are preserved correctly. No new logic introduced since the previous approval. Still solid.

@thomasluizon
thomasluizon merged commit bd0218c into main Jun 5, 2026
4 checks passed
@thomasluizon
thomasluizon deleted the feature/describe-feature branch June 5, 2026 21:29
thomasluizon added a commit that referenced this pull request Jun 27, 2026
…ing (#261)

* feat(gamification): free-tier rebalance + infinite levels + recap (#186, #190)

Reversible gamification_free_tier AppFeatureFlag (default off) unlocks streak/XP/level + streak-freeze auto-activation for free users. Infinite levels via 100*L^2 curve, continuous past level 10. New half_year_hero/streak_titan achievements + reserved first_cheer. GET /api/gamification/recap returns metrics-only retrospective + referral shareDeepLink, ungated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(astra): warm persona tone + soften act-immediately framing (#183)

New EncouragingToneSection (Order 150) + softened CoreIdentitySection so clarify-first coexists with direct action; destructive/bulk route through existing confirmation-card gating. Reuses shipped resolve + pendingOperations infra; gpt-4.1-mini unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(social): social foundation - friendships, cheers, feed, moderation, handles (#193)

5 entities + User.Handle/SocialOptIn + migration w/ deterministic handle backfill. Endpoints (request/accept/remove/list/feed/cheer/block/report + set-handle + opt-in), SocialAccessGuard, rate limits. OpenAI moderation (fail-open on outage). FriendFeedEvent write-pipeline: streak hook (all users) + achievement hook (Pro) + keyset read. first_cheer wired; FCM pushes; export + deletion purge. Backend + shared only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(onboarding): setup-checklist flags, bulk-create tags, template-pack support (#187, #189)

User onboarding-checklist completion flags (ride profile payload, auto-set from signals) + onboarding achievement hook (#189). BulkCreateHabits accepts per-item tags resolved/created by name for starter packs (#187). Migration AddOnboardingChecklistFlags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(api): address PR #261 review — block-filter cheers, unblock validator, feed leak, period 400 (#193, #190)

Resolves claude-review findings: add UnblockUserCommandValidator (HIGH); filter blocked users from GetCheersQuery both directions (HIGH); drop opted-out actors from the friend feed (MED); MaximumLength on friend-request handle/referralCode (MED); recap period set now matches the resolver via a shared IsKnownPeriod (single source of truth) + an invalid period returns 400 not 500 at the recap/retrospective endpoints (MED).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(api): #261 round-2 review — block/unblock rate limits, GetFriends validator, ordering, cheer-ownership

Rate-limit block/unblock (50/24h); add GetFriendsQueryValidator (UserId NotEmpty); check target existence before BlockedUser.Create; verify a reported CheerId involves the reported user (else CheerNotFound).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant