Skip to content

fix(chat): make the FAQ cache reachable + stop double-emitting pending ops#247

Merged
thomasluizon merged 1 commit into
mainfrom
fix/astra-faq-cache-and-calendar-dedup
Jun 25, 2026
Merged

fix(chat): make the FAQ cache reachable + stop double-emitting pending ops#247
thomasluizon merged 1 commit into
mainfrom
fix/astra-faq-cache-and-calendar-dedup

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

Two backend Astra fixes found while validating the #301 epic against prod logs. Paired client fix (calendar text-leak) ships in orbit-ui-mobile.

1. FAQ response cache was write-dead

Prod logs showed asking "what is a streak freeze?" twice still made two full ~45-tool model calls — the cache never hit. Root cause: the store condition required a zero-tool turn (iterations == 0), but system-prompt rule #19 directs the model to call describe_feature for exactly the cached topics, so the condition was structurally unreachable.

  • Cache an answer when the turn raised no pending confirmation and every tool it ran (if any) was a successful describe_feature — a static, user-data-free lookup, so the answer is safe to share across users. Called tool names are now tracked on the execution accumulator (IsShareableFaqTurn).
  • Allow-list describe_feature specifically — not "any read-only tool", since user-specific reads (get_streak_info, …) must never be cached cross-user.
  • Locale fix: key the cache by the language the question was asked in (derived from which EN/PT phrase matched) instead of the user's profile language. The model answers in the message's language, so the old key could serve an English answer to a Portuguese asker (and vice-versa).

2. Calendar confirmation double-emitted the operation

A confirmation-required op (e.g. manage_calendar_sync) was emitted into both Operations and PendingOperations. Drop the redundant Operations copy for PendingConfirmation — the pending operation is the canonical representation of a not-yet-run op. (Clients don't render Operations, so this is a latent-contract cleanup; the user-visible leaked text bubble is fixed client-side in the paired PR.)

Tests

  • FAQ matcher returns key + question locale (EN/PT).
  • IsShareableFaqTurn: describe_feature-only ✓, no-tools ✓, user-data tool ✗, failed describe_feature ✗, pending confirmation ✗ (unit-tested via InternalsVisibleTo — avoids the static-cache parallelization hazard a handler test would introduce).
  • The pending-op handler test now asserts the op is not duplicated into Operations.
  • Full suite green: 3,678 tests.

🤖 Generated with Claude Code

…g ops

The FAQ response cache was write-dead: it only stored answers from zero-tool turns, but the system prompt directs the model to call describe_feature for exactly the cached topics, so the store condition was never satisfied and the cache never populated.

- Cache a FAQ answer when the turn raised no pending confirmation and every tool it ran (if any) was a successful describe_feature — a static, user-data-free lookup, so the answer is safe to share across users. Called tool names are now tracked on the execution accumulator; the allow-list is describe_feature specifically (never 'any read-only tool', since user-specific reads must not be cached). - Key the cache by the language the question was asked in (derived from which EN/PT phrase matched) rather than the user's profile language, since the model answers in the message's language — stops serving a wrong-language answer across users.

Calendar dedup: a confirmation-required operation was emitted into BOTH Operations and PendingOperations. Drop the redundant Operations copy for PendingConfirmation; the pending operation is the canonical representation of a not-yet-run op.

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

Copy link
Copy Markdown

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

Two precise bug fixes backed by solid tests. The FAQ cache fix is well-designed: deriving locale from which language's phrase matched is strictly more correct than using the user's profile language (the model responds in the message language per system-prompt rule #2, so the old profile-language key could store an English answer under a Portuguese key for bilingual users). The IsShareableFaqTurn guard is correctly layered — PendingOperations.Count == 0 catches confirmation-required mutations, CalledToolNames.All(name == describe_feature) catches any non-static read by name (unconditionally recorded before any conditional branching in Add), and OperationResults.All(Succeeded) provides a tertiary backstop for failed describe_feature calls. The pending-confirmation double-emit fix is safe: PendingOperations is the complete, canonical field for the confirmation flow and Operations carries only audit data that clients don't render. Test coverage is thorough and correctly exercises all five shareability cases.

@thomasluizon
thomasluizon merged commit 9e9faa6 into main Jun 25, 2026
8 checks passed
@thomasluizon
thomasluizon deleted the fix/astra-faq-cache-and-calendar-dedup branch June 25, 2026 02:54
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