feat/issues-64-67-modules-41-43 - #185
Merged
Darkvader-ship-it merged 1 commit intoAug 31, 2026
Merged
Conversation
|
@LamsOfJos Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements issues #64, #65, #66, #67 as isolated, feature-flagged domain modules
following the repo's established pattern (module in
lib/*-store.ts+ additiveroute wiring + UI wiring +
node:testsuite). All three new flags —phase-136,phase-137,phase-138— default off, so every route and UI path isbyte-identical to
mainuntilNEXT_PUBLIC_FEATURE_PHASE_136/137/138(orFEATURE_PHASE_*) is set.Issue #64 — CID gateway resolution cache with TTL + health scoring (
phase-136)Every metadata read re-resolved a CID against the gateway list from scratch, and
a degrading gateway kept being picked until it hard-failed.
lib/signal-store.ts:resolveCidGateway(cid, opts)memoizes theCID→gateway URL per CID with a TTL (default 5 min, bounded 1 s–24 h).
recordCidGatewayOutcome({ gateway, ok, latencyMs })feeds a rolling healthmodel (success ratio 70% + EWMA latency 30%); a recorded failure evicts every
cache entry pinned to that gateway. Flag off ⇒ deterministic first-gateway pick,
no caching. Typed
CidResolutionError+ zod schemas.app/api/signals/[id]/replies/route.ts: POST + GET responses carry cachedsignalMedia: { image, gateway }and anX-Phase136-Gatewayheader when thesignal has an IPFS
nft_image.app/signals/[id]/page.tsx: renders the resolved gateway URL, falling backto the stored URL on any failure.
Issues #65 & #66 — Structured error taxonomy for avatar / x402 failures (
phase-137)#65 and #66 are duplicates (same title, same file set); one coherent
implementation covers both — the server taxonomy and the client retry policy.
Avatar / gateway / invoice failures all surfaced as a single generic
500 Internal server error, so a Pinata timeout, a checksum mismatch and a badwallet were indistinguishable in logs.
lib/profile-store.ts:ProfileErrorcarryingcode, deterministicstatus,categoryandretryable; closed 13-code taxonomy;classifyProfileError(err)maps AbortError/timeouts →GATEWAY_TIMEOUT(504,retryable), DNS/conn failures →
GATEWAY_UNREACHABLE, checksum/tamper →CHECKSUM_MISMATCH,{ status }upstream errors → 4xx/5xx/429,ZodError→MALFORMED_RESPONSE, elseINTERNAL.toProfileErrorResponse(err)+ProfileErrorResponseSchema.app/api/profile/avatar/route.ts: GET/POST catch blocks and POSTfetch/pin-failure branches emit
{ error, code, category, retryable }with thetaxonomy's status when the flag is on; legacy 500 otherwise.
components/wallet-avatar.tsx: does one silent retry on a retryableupstream code before settling on initials; surfaces the final code via
data-avatar-error/title. Fetch is now abortable.Issue #67 — Cost attribution ledger per request (
phase-138)Infra spend (Horizon fan-out on the follow-suggestions path, notification writes,
profile enrichment) was never attributed to the request that caused it, so the
treasury couldn't reconcile spend against revenue.
lib/follow-store.ts:recordRequestCost({ requestId, operation, count?, units?, source?, wallet? })appends to a bounded (5,000-entry) in-memoryledger; flag off ⇒ no-op returning
0.BILLABLE_OPERATIONStaxonomy withdefault unit weights,
getRequestCost,getCostLedger,summarizeCostByOperation(treasury view). TypedCostAttributionError+ zodschema.
app/api/profile/follow/route.ts: suggestions path booksfollow.suggestions+horizon.*+ per-profileprofile.enrichmentcost andreturns
costUnits+X-Phase138-Cost-Units; follow/unfollow POST booksfollow.write(+notification.create) and returnscostUnits.app/profile/[wallet]/follow-button.tsx:FollowSuggestionssurfaces therequest cost as
STELLAR GRAPH · <n>u.isFeatureEnabled,FollowSuggestionQuerySchemaandgetFollowSuggestionswithout importingthem — the suggestions endpoint threw
ReferenceErrorat runtime. Importsadded.
Verification
npx tsc --noEmit: 65 → 43 pre-existing errors (net −22, from thefollow-route missing imports); no new type errors in any changed file.
+25new tests, 0 regressions (the6 pre-existing failures —
narrative-search,forge-pipeline,watchlist-price-drops— are unchanged and unrelated).eslint: no new errors or warnings in changed files.See
IMPLEMENTATION_SUMMARY_64_67.mdfor the full breakdown.Closes #64
Closes #65
Closes #66
Closes #67