Skip to content

feat(api): surface describe_feature related_surfaces on ChatResponse - #187

Merged
thomasluizon merged 2 commits into
mainfrom
feature/wave2-chat-rendering
Jun 6, 2026
Merged

feat(api): surface describe_feature related_surfaces on ChatResponse#187
thomasluizon merged 2 commits into
mainfrom
feature/wave2-chat-rendering

Conversation

@thomasluizon

@thomasluizon thomasluizon commented Jun 5, 2026

Copy link
Copy Markdown
Owner

What

The describe_feature chat tool returns related_surfaces in its read-only ToolResult.Payload (e.g. ["gamification", "today"]), and the executor preserves it on AgentOperationResult.Payload. But BuildActionResult returns null for read-only tools, so the surfaces were dropped before reaching the client — the assistant only re-emitted the markdown body as text.

This threads them through:

  • Add optional IReadOnlyList<string>? RelatedSurfaces to ChatResponse.
  • In ToolExecutionAccumulator, collect related_surfaces from successful read-only tool payloads (round-tripped through JsonSerializer to a JsonElement), deduped in first-seen order.
  • Assemble onto ChatResponse (null when empty, so an older client degrades gracefully).
  • BuildActionResult still returns null for read-only tools — the footer rides on RelatedSurfaces, not on an ActionResult chip.

Why

Part of Orbit Wave 2 PR-B. The UI renders these app-surface IDs as a "Related" deep-link footer in chat (web + mobile).

Tests

  • ProcessUserChatCommandHandlerTests: new cases assert RelatedSurfaces is populated after a describe_feature-style read-only payload and left null on a mutating-only turn.
  • dotnet build (Orbit.slnx) clean; dotnet test tests/Orbit.Application.Tests green (35/35 in the handler suite).

Cross-repo

Refs thomasluizon/orbit-ui-mobile#95

Paired UI PR (consumes RelatedSurfaces, renders the footer, unifies markdown rendering, adds new-tool chips): thomasluizon/orbit-ui-mobile#139

🤖 Generated with Claude Code

The describe_feature tool returns `related_surfaces` in its read-only payload,
but BuildActionResult returns null for read-only tools so it never reached the
client. Extract `related_surfaces` from successful read-only tool payloads in
the tool-execution accumulator (deduped, first-seen order) and thread it onto a
new optional `ChatResponse.RelatedSurfaces`. The client maps these app-surface
IDs to deep links in a "Related" chat footer.

Refs thomasluizon/orbit-ui-mobile#95

Paired UI PR (consumes RelatedSurfaces, renders the footer):
thomasluizon/orbit-ui-mobile feature/wave2-chat-rendering

Co-Authored-By: Claude Opus 4.8 <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.

Threads related_surfaces from read-only tool payloads through to ChatResponse.RelatedSurfaces cleanly. The extraction logic correctly handles the JSON round-trip, guards against non-object/missing-array payloads, and deduplicates in first-seen order. The null-when-empty sentinel keeps older clients unaffected. Unit tests cover both the happy-path (surfaces populated) and the negative case (mutating-only turn leaves the field null). No correctness, security, contract, or hard-rule issues found.

@thomasluizon
thomasluizon merged commit 9c8d748 into main Jun 6, 2026
4 checks passed
@thomasluizon
thomasluizon deleted the feature/wave2-chat-rendering branch June 6, 2026 00:51
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