Skip to content

Build 22: response-delivery fixes (push-embed + pending relay-URL)#10

Merged
DocNR merged 5 commits into
mainfrom
fix/response-delivery
Apr 21, 2026
Merged

Build 22: response-delivery fixes (push-embed + pending relay-URL)#10
DocNR merged 5 commits into
mainfrom
fix/response-delivery

Conversation

@DocNR
Copy link
Copy Markdown
Owner

@DocNR DocNR commented Apr 21, 2026

Summary

Two bug fixes bundled for build 22 — both on the "response delivery reliability" surface surfaced by build 21 matrix testing:

  • Payload-embed (proxy + NSE + foreground): proxy now size-gates the caught kind:24133 event into the APNs push payload (≤3500B). NSE and foreground push handler prefer the embedded event over the existing fetch-from-relay path, closing the ephemeral-drop race that made fevela / noStrudel / Coracle flake on signing.
  • Pending relay-URL (SharedModels + LightSigner + AppState): PendingRequest now stores the origin relay URL (responseRelayUrl: String?), so approve-later publishes land on the relay the client is actually subscribed on instead of the relay.powr.build fallback. Latent bug since PR fix(signer): switch_relays returns null (NIP-46 responder-only) #7 (switch_relays → null) — nostr-tools-family clients stopped migrating to powr.build, so the fallback stopped matching. Bunker flow was unaffected because Clave's bunker URIs are pinned to powr.build.

Design doc: `/hq/clave/specs/2026-04-20-response-delivery-fixes-design.md`
Plan: `
/hq/clave/plans/2026-04-20-response-delivery-fixes.md`

Changes

  • `relay-proxy/proxy.js`: ~7 lines in `dispatchCaughtEvent` — size-gate `event` embed in push payload
  • `ClaveNSE/NotificationService.swift`: ~15 lines in `handleSigningRequest` — prefer embedded event, fall through to fetch; distinguish cast-miss log from key-missing log
  • `Clave/ClaveApp.swift`: ~15 lines in `handleForegroundSigningRequest` — same pattern as NSE
  • `Shared/SharedModels.swift`: +1 field `responseRelayUrl: String?` on `PendingRequest` (Optional → default Codable synthesis handles pre-build-22 rows as nil)
  • `Shared/LightSigner.swift`: capture `responseRelayUrl` into PendingRequest at queue-time
  • `Clave/AppState.swift`: thread `request.responseRelayUrl` back to `handleRequest` at approve-time

~30 lines of net diff across 6 files. 5 commits (2 of which are small quality-review follow-ups).

Backward-compatible in both skew directions (build 21 app + build 22 proxy, build 22 app + build 21 proxy). Pre-build-22 pending rows decode with `responseRelayUrl: nil` and fall back to powr.build — unchanged broken behavior for pre-upgrade stragglers, accepted per spec as one-time degradation.

No new unit tests — manual matrix verification only for build 22 per sprint decision. Backlogged: `push-payload.test.js` (proxy) and `NotificationServiceTests` (iOS). Local Node 18 dev env has a pre-existing `nip98.test.js` failure (ESM `require` incompat with `@noble/curves@2.2.0` — Dell runs Node 20+ where this passes); not introduced by this PR.

Test plan

  • Existing proxy tests pass locally on Node 20+ (Dell deploy target)
  • Xcode `Cmd+U` — all existing iOS unit tests pass
  • Deploy proxy to Dell, verify `[Push]` log line on first caught event post-restart
  • Build 22 installed via internal TestFlight
  • Matrix:
    • Nostur bunker kind:1 auto-sign (regression)
    • Nostur bunker kind:10002 pending approval (regression)
    • fevela nostrconnect cold-start kind:1 (post-5-min-wait) — must work
    • fevela nostrconnect kind:10002 pending approval — must work
    • noStrudel nostrconnect kind:1 — must work
    • Coracle nostrconnect kind:1 (established npub) — must work
    • Coracle nostrconnect kind:10002 pending approval — must work
    • Cap boundary: 6th pair rejected
    • Offline pair: pending pair op drains on resume

🤖 Generated with Claude Code

DocNR added 5 commits April 20, 2026 22:13
Avoids the NSE ephemeral-fetch race when the relay drops kind:24133
between proxy dispatch and NSE wake. Oversized events fall through
to the existing fetch path (no regression).

Part of build 22 response-delivery hotfix. See
~/hq/clave/specs/2026-04-20-response-delivery-fixes-design.md
NSE and AppDelegate foreground-push handler now check userInfo["event"]
first; fall through to LightRelay.fetchEvents only if absent. Closes
the ephemeral-fetch race for ≤3500B events. Oversized events fall
through as today.

Part of build 22 response-delivery hotfix.
Spec Risk #1 mitigation: when userInfo["event"] is present but not a
dict, log a warning so proxy payload-shape regressions are visible
in production triage. Unchanged: behavior on happy path + missing-key
path.
PendingRequest gains relayUrl: String? captured at queue-time from the
incoming responseRelayUrl. approvePendingRequest threads it back into
LightSigner.handleRequest, so signed responses for protected kinds
publish to the relay the client is actually subscribed on — not the
powr.build fallback.

Bug was latent since PR #7 (switch_relays → null) — nostr-tools clients
stopped migrating to powr.build, so the fallback stopped matching.
Bunker flow unaffected because Clave's bunker URIs are already pinned
to powr.build.

Codable's default synthesis handles missing keys on Optional fields,
so pre-build-22 pending rows decode as nil → fall back to powr.build
(unchanged broken behavior for pre-upgrade stragglers).

Part of build 22 response-delivery hotfix.
Matches the parameter name on LightSigner.handleRequest so the
call site reads `responseRelayUrl: request.responseRelayUrl` instead
of `responseRelayUrl: request.relayUrl` — self-documenting round
trip. Also corrects "forward" → "backward" compatibility in the
field's doc comment (new code reading pre-upgrade rows is backward
compat, not forward).
@DocNR DocNR merged commit 6fd84fb into main Apr 21, 2026
@DocNR DocNR deleted the fix/response-delivery branch April 21, 2026 03:32
DocNR added a commit that referenced this pull request May 2, 2026
Brainstorm review of design-system.md against shipped code surfaced 9
inconsistencies + 1 anti-pattern still present. Fixed everything in one
batch so the next TestFlight archive carries it all.

Code:
- HomeView: drop .padding(.bottom, 8) on SlimIdentityBar invocation —
  slim banner owns its outer bottom padding (12pt); stacking another 8pt
  on top was double-counting (review #4)
- HomeView: drop .padding(.bottom, 8) inside statsRow — listSectionSpacing(0)
  carries the gap to Connected Clients; the residual padding kept the
  visible gap excessive after polish round 2 (review #2)
- AccountDetailView: avatarLarge letter fallback opacity 0.25 → 0.22 to
  align with SlimIdentityBar's 0.22 (review #3)
- ConnectSheet: add .presentationBackground(Color(.systemGroupedBackground))
  — was the last sheet still defaulting to translucent (review #9)
- ApprovalSheet: rename @State capExceeded → showConnectionCapAlert for
  naming convention parity with HomeView (review #7)

design-system.md:
- New "Cross-platform applicability" section at the top — clarifies what
  carries directly to clave.casa web companion (color tokens, displayLabel
  rule, identity-vs-functional zone philosophy, avatar treatments, copy
  patterns, anti-patterns) vs what's iOS-only (SwiftUI modifiers, haptics,
  sheet/toolbar conventions)
- §3 Typography: corrected initial-letter font scale — AvatarView uses
  size*0.35 mono (pubkey) or size*0.4 proportional (name); was wrongly
  documented as a single 0.37 (review #1)
- §4 Avatars: added Treatment Selection Rule table (B on neutral bg,
  C on saturated theme gradient) + clarified 1-vs-2 letter behavior
  (review #5, #6)
- §4 Sizing scale: expanded table to include initial font + border
  thickness per slot, with the ~5% border scaling rule (review #8)
- §5 Spacing: explicit "single source of truth" note on slim banner
  bottom padding; new "Stats row" subsection capturing the
  ultraThinMaterial-on-small-cards-OK rule (review gap #10, #4)
- §6 HomeView gradient: documented palette[0] defensive fallback when
  currentAccount is nil (review #13)
- §7 Patterns: new "State variable naming" subsection with the
  showCapAlert / showAccountCapAlert / showConnectionCapAlert
  convention (review #7 doc side)
- §11 Anti-patterns: audit-point note that ConnectSheet was the last
  surface to acquire .presentationBackground (review #9 doc side)

Build green on iOS Simulator 26.4. pbxproj still 41 — assumes user hasn't
yet archived 41; bump to 42 if needed before re-archive.

Co-Authored-By: Claude Opus 4.7 (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