Skip to content

RespondKit 0.6.2: no email-ask flash, Android attach icon, catalog strings - #56

Merged
lhr0909 merged 22 commits into
mainfrom
developer/rk-1
Oct 15, 2026
Merged

lhr0909 merged 22 commits into
mainfrom
developer/rk-1

Conversation

@lhr0909

@lhr0909 lhr0909 commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Why

Simon tested RespondKit 0.6.1 in ReceiptGenie's iOS and Android apps and found three problems (RK-1):

  • The email ask flashed even when the email was known.
  • Android's attach control was a text button above the input.
  • The empty state, the subtitle and the attach menu were plain literals outside RespondKit's i18n toolchain.

ReceiptGenie 2.9.0 should ship on the fixed SDK.

What changed

  • Email-ask flash (asksForEmail on the iOS store, Android SupportState and the RN snapshot): the ask shows only after the contact lookup for the current identity has succeeded without finding an email. A host-supplied email counts as known immediately, and updateIdentity resets the state. A failed or canceled lookup keeps the ask hidden, and resolveContact retries it on the next request, including when the session is cached (review findings 1–2). The epoch is rechecked after that step, so an identity switch during a failed lookup can't leak the old session's token (follow-up P1). Before, iOS, Android and RN rendered the ask on email == nil while a stored contact was still being fetched.
  • Web: no code change. The hook already reports resolving_context whenever the context doesn't match and sets the email before ready. Two MutationObserver tests now guard this; I confirmed they fail when the prompt does mount.
  • Android attach: a 44dp paperclip IconButton in the composer row, left of the field and aligned with send. The photo/files menu stays anchored to it.
  • Strings: one validated catalog feeds every client.
    • iOS: the generator writes RespondKit.xcstrings into RespondKitUI/Resources (defaultLocalization: "en", .process("Resources")).
    • Android: Compose adds localization/generated/android/res as a res dir and drops its own strings.xml.
    • Web and RN: compileJavaScriptMessages (in @respondkit/paraglide-resources, validated by the same parseResources) emits a typed messages(locale) module into packages/react/src/widget/messages.ts and packages/react-native/src/messages.ts. localization:check byte-checks both, and the npm files lists ship them.
    • On every client the empty state, "Ask us anything" and the attach menu (plus its a11y label) read from the catalog. Web picks by context.locale, RN by the device locale.
    • The macOS localization workflow compiles the catalog at its new path (review finding 4).
  • 0.6.2: pnpm version:set 0.6.2 and docs/releases/0.6.2.md. Not published, and no GitHub release.

Scope

  • Only the surfaces named on the card. Other strings ("Write a message…", the email prompt, Retry and so on) are still literals on every client.
  • Locales: every SDK ships English only (settings.json has locales: ["en"]), so "every shipped locale" means en. This PR adds no new languages. Translating into ReceiptGenie's languages needs a decision and a follow-up.
  • Web and RN don't use Paraglide's own JS compiler: it loads the inlang project, whose plugin module is a CDN URL in settings.json, at build time.

Tradeoffs

  • Commits are per platform, not per issue, because each platform's screen file carries all three fixes.
  • The Compose strings.xml was deleted rather than kept in parallel, because duplicate resource names in one module fail the merge.

Blast radius

  • Android public resources: respondkit_new is removed (use respondkit_new_conversation), and respondkit_intro now holds the longer intro line. Hosts that override either name must update their overrides. ReceiptGenie doesn't.
  • AttachmentPicker (internal) now takes a content slot. MessageComposer (web) takes an optional locale.
  • While the contact lookup keeps failing (an older server without the contact endpoint, or an outage), every request on the session retries it and the ask stays hidden.
  • The root package.json adds the @respondkit/paraglide-resources workspace devDependency; the lockfile change is 3 lines.
  • The SwiftPM RespondKitUI target now has a resource bundle.
  • No API or protocol change. The only apps/api and packages/discord diffs are the 0.6.2 version bumps; /approve and the Discord commands are untouched (a separate PR redesigns them).

Verification

  • pnpm exec vp check passes.
  • pnpm localization:check matches.
  • pnpm test:release passes.
  • pnpm test:all passes: 410 tests at 1c7f181 (paraglide-resources 37 with the new exporter test, react-native 20 with the retry and identity-race cases, API 159).
  • swift test: 24 tests in 3 suites, including the failed-lookup (2 cases), canceled-lookup and identity-switch-during-failed-lookup tests; each fails on the store before its fix. The iOS simulator build of RespondKitUI succeeds, and the built bundle contains en.lproj/RespondKit.strings with all six keys.
  • ./gradlew :core:test :compose:assembleRelease :compose:lint :compose:assembleAndroidTest: BUILD SUCCESSFUL, with 24 core tests including the failed, canceled and identity-switch lookup tests. The identity-switch test fails at c653a6c with 1 stale statuses call.
  • :compose:connectedDebugAndroidTest on an API 36 emulator: 6/6, including the new attachIconSitsLeftOfTheMessageFieldInTheSendRow.
  • Screenshots from the example apps against native/examples/demo_server.py are attached to RK-1 on the board:
    • Android composer and menu: paperclip left of the input.
    • iOS empty state: catalog strings resolved.
    • The demo server has no stored email, so the ask correctly shows after lookup.
  • CI at 1e58df7: every check passes, including verify-npm. The smoke install now pins the React Native peers to the tested versions (react-native 0.87.1, device-info 15.0.2, get-random-values 2.0.0, safe-area-context 5.10.1). Before, it resolved react-native 0.88.0, which needs react ^19.3.0 and failed with ERESOLVE against the pinned 19.2.8.
  • npm pack --dry-run includes src/widget/messages.ts (react) and src/messages.ts (react-native).
  • Not done: no web or RN UI run after the string wiring (the web tests render the strings), no screenshot of the open iOS attach menu (simctl can't tap), and no on-device check inside ReceiptGenie itself.

🤖 Generated with Claude Code

lhr0909 and others added 12 commits October 15, 2026 23:23
SwiftPM resources must live inside the target path, so the generator now
writes RespondKit.xcstrings to RespondKitUI/Resources and Package.swift
processes it with defaultLocalization en. localization:check still
covers both outputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t lookup

Fails today: the screen shows the ask while a stored email is still
being fetched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
asksForEmail turns true only after the session's contact lookup finds no
email, and resets on updateIdentity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A MutationObserver records every mounted prompt while a host-supplied
context changes and when the server already stores an address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he catalog

asksForEmail is true only once the contact lookup for the current
identity found no email. The empty state, Ask us anything and the
attach menu read the RespondKit table from the module bundle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h, shared strings

- The attach control is a 44dp paperclip IconButton in the composer row,
  left of the field and aligned with send; the menu stays anchored to it.
- SupportState.asksForEmail replaces the emailAddress null check.
- Compose takes its strings from localization/generated/android/res and
  drops its own strings.xml; the empty state, subtitle and attach menu
  use stringResource.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every package at 0.6.2 (Android reads VERSION, SwiftPM uses v0.6.2);
docs/releases/0.6.2.md. Not published.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dy sent

Fails today with 'That message is not an agent draft in this support
thread.' for the reply's available_audit Discord message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nt draft'

/approve (Discord id or msg_agent id) and the Approve/Reject draft
commands look up the reply's available_audit projection; an agent
message available to the customer answers with when it was sent and
points to /reply. Other ids keep 'not an agent draft'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lhr0909 lhr0909 changed the title RespondKit 0.6.2: no email-ask flash, Android attach icon, catalog strings RespondKit 0.6.2: no email-ask flash, Android attach icon, catalog strings, already-sent /approve answer Oct 15, 2026
Simon moved /approve to a separate redesign PR that owns
apps/api/src/agent-replies.ts, http.ts and the Discord command
definitions, so this PR keeps to the SDK fixes and 0.6.2 prep. Reverts
8e57e5f, 0c128cd and 9475ccc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lhr0909 lhr0909 changed the title RespondKit 0.6.2: no email-ask flash, Android attach icon, catalog strings, already-sent /approve answer RespondKit 0.6.2: no email-ask flash, Android attach icon, catalog strings Oct 15, 2026

@simon-langoustine simon-langoustine left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT changes-requested / verifier-failed at head 1de16ef23a576e9b0eab1ba86e0ff191f01b5854, compared with fixed base cd38add8d98719db3c626fce74a0bc0716e7b2fe.

Four act-on findings are detailed in the inline threads:

  1. Failed contact lookups mark the email as resolved and show the ask even when the server stores an address. Healthy refreshes do not retry the lookup. Reproduced with the actual RN store/API client and in the Android example.
  2. Canceling the first native contact lookup leaves contactResolved false behind a cached session. Reopening and refreshing never resolves it, so customers without email never see the ask. Reproduced against unchanged Swift sources; Android has the same sequence.
  3. The specified web/RN empty-state, subtitle and attachment strings still bypass the shared localization toolchain. Only English currently ships, so additional languages are not required; removing the named literals is required.
  4. The Swift catalog moved, but the macOS localization workflow still compiles its deleted path. The command fails locally and in this head's CI.

Verification passed locally: pnpm exec vp check; pnpm test:all with 406 tests; localization:check; test:release with 8 tests; release:check; swift test with 21 tests; Android core tests, Compose release build/lint/instrumentation build and example debug build; all 6 connected Compose tests; iOS example simulator build. Version wiring is consistent at 0.6.2. The excluded API and Discord source files are unchanged.

Live UI: drove Android's empty state, paperclip menu, system Files picker and selected-file row, and reproduced the contact-error behavior. Drove web sending, close/reopen, a second send at 390x844, file-upload failure/removal and email-save failure. Android layout looks aligned. Local fixtures were used; no customer-facing sends occurred. The attachment fixture returned a precompleted upload, so this is not proof of real file transfer.

Consider: verify-npm also fails because its smoke installation combines React 19.2.8 with automatically selected React Native 0.88.0, which requires React ^19.3.0. The checker and peer constraints are unchanged here; resolve or track this before publishing.

Noted / limits: interactive iOS testing was blocked by simulator launch delays and an agent-device XCTest runner timeout. RN UI and ReceiptGenie host integration were not run. CONTEXT.md, the project verify skill and feature map were absent; used the native demo instructions and independent consistency reviewers. Interrogate's configured Claude/Grok models were unavailable, so GPT-6.1 Sol and GPT-6 Astra reviewed independently; both identified cancellation and localization issues. No product code was edited, pushed, merged or published.

Evidence is attached to RK-2: logs and runnable reproductions, Android layout, attach menu, selected file, incorrect ask after recovery, and web narrow-screen second send. Board links require access to the local Office instance.

Comment thread packages/react-native/src/store.ts
Comment thread native/swift/Sources/RespondKitCore/RespondKitStore.swift
Comment thread localization/README.md Outdated
Comment thread scripts/generate-localizations.ts
lhr0909 and others added 6 commits October 15, 2026 23:53
The macOS job still pointed at localization/generated/ios, which the
catalog move deleted. It now compiles RespondKitUI/Resources and runs
when that catalog changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A failed lookup must keep the ask hidden and retry on the next request,
and a lookup canceled behind a cached session must resume. Fails today:
both mark the contact resolved or never retry it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ached session

validToken/token now runs resolveContact for cached and new sessions
alike. A failed or interrupted lookup leaves the contact unknown, so the
ask stays hidden and the next request retries it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
compileJavaScriptMessages validates through parseResources like the
native exporters and emits messages(locale) with typed functions and
Intl plural selection, falling back to the closest shipped locale. The
generator writes it into both packages; localization:check covers it,
format ignores it, and the npm files lists ship it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…catalog

Web picks by context.locale, React Native by the device locale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@simon-langoustine simon-langoustine left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT changes-requested / verifier-failed at head c653a6c9643a28701a7ab549e2bb3445b45b3c39.

Re-review is limited to the four findings from 1de16ef and the stated iOS/RN UI gaps. The four original findings are addressed:

  • Failed contact lookup keeps the ask hidden and retries on a cached session.
  • Canceled native lookup resumes on subsequent use; new regression tests cover it.
  • The named web/RN strings now consume generated canonical catalogs; generation checks and package file lists cover them.
  • The Apple localization workflow uses the moved catalog. Its compile/runtime check passed locally, and macOS CI passes.

One new act-on issue in the retry fix blocks approval: a failed contact lookup skips the identity-generation check before an API write. Both independent reviewers found this. Using the actual RN store and API client, I reproduced a queued message POST to user A's existing thread with A's token after updateIdentity(B), before logout. The same reproduction produces 1 stale write on this head and 0 on the previous head. A second reproduction shows saveContact after the switch. Swift and Kotlin contain the same unchecked failure return. The inline thread identifies the fix and required test.

The previous iOS/RN execution gaps are now closed for the local demo flows:

  • iOS: built this head and used the working agent-device runner. Checked empty state, email ask after lookup, attach menu, system Files picker/cancel, send/reply, close/reopen through the alternate host trigger, and save-email prompt dismissal. With contact returning 503, the ask remained hidden; changing the fixture to a successful empty contact made it appear on a later poll without resetting the store.
  • RN Android: built this head, used its Metro bundle, sent RK2, received the demo reply, opened the catalog-backed attach alert and system Files picker. The demo's custom greeting replaces its no-greeting empty state; that branch was checked in code and catalog tests.
  • Web: sent a message, checked the catalog-backed subtitle/attach label, reopened, and checked 390x844 layout.

Passed locally: vp check; all 409 package tests; localization:check; 23 Swift tests; 23 Android core tests; Compose release build/lint and example debug build; native iOS simulator build; RN Android debug build; Apple catalog compile/runtime tests. Standard suites do not cover the newly reproduced rejected-lookup identity race.

Consider: the unchanged npm smoke peer-dependency conflict still fails verify-npm. No new act-on finding beyond the identity race. No ReceiptGenie host integration or real file-transfer check; all sends used local fixtures. Android multiword input was truncated by the adapter, so only the actual RK2 send is claimed. No product source changes, push, merge or publication.

Evidence on RK-2: logs and runnable head/baseline reproductions, iOS attach menu, iOS send/reply, failed contact hidden, recovered contact ask, RN send/reply, RN attach menu, web narrow layout. Board links require the local Office instance.

Comment thread packages/react-native/src/store.ts
lhr0909 and others added 2 commits October 16, 2026 00:12
…ing from the old session

Fails at c653a6c: the failed lookup returns before the epoch check, so
the old identity's token reaches the next request (RN saveContact,
Swift/Kotlin statuses).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
resolveContact can return early on a failed lookup, so token/validToken
now checks the epoch before handing out the session token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@simon-langoustine simon-langoustine left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at 1c7f1816ef84d0e19aca60eab5aafc3ee34381f4 — unit-test-verified.

Scoped re-review of the remaining P1 identity race, from fixed point c653a6c9643a28701a7ab549e2bb3445b45b3c39.

The RN, Swift, and Kotlin token paths now recheck the identity epoch immediately after resolving contact, before returning the token to an authorized operation. A failed contact request can no longer let the old identity perform the pending operation after an identity switch. The new regression tests cover this interruption. Both independent reviewers found no remaining issue in this six-file delta.

Act-on: none. The remaining P1 is resolved; the four original findings were resolved in the preceding review.

Verification at this head:

  • React Native: 20 tests passed.
  • Swift: 24 tests passed.
  • Kotlin core: 24 tests passed.
  • pnpm exec vp check: passed.
  • Both prior adversarial reproductions rerun against current source: zero stale message writes and zero stale contact saves after switching identity. Logout of the old session still occurs as intended.

Consider: the existing verify-npm smoke-install failure remains a pre-publication issue (React 19.2.8 / automatically selected RN 0.88 peer conflict), outside this scoped logic fix. Current failing job.

Noted: unchanged screens were not driven again at this head. The preceding review closed the iOS adapter and RN UI gaps at c653a6c9643a28701a7ab549e2bb3445b45b3c39; that review records the UI coverage and limits. This approval is for the scoped pure-logic follow-up, not a store-release or publication verdict. No product code edits, merge, or publication performed.

Evidence: RK-2 logs and standalone reproductions (Office board access required).

The smoke project named no react-native, so npm picked the newest
release in the >=0.81 peer range. react-native 0.88.0 needs react
^19.3.0 and the install failed with ERESOLVE against the pinned
19.2.8, blocking verify-npm and publish-npm. Install the versions the
SDK is built and tested with (0.87.1 and its locked companions).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lhr0909
lhr0909 merged commit dc4c6a2 into main Oct 15, 2026
12 checks passed
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.

2 participants