Skip to content

feat/issues-64-67-modules-41-43 - #185

Merged
Darkvader-ship-it merged 1 commit into
PHASE-STELLAR:mainfrom
LamsOfJos:feat/issues-64-67-modules-41-43
Aug 31, 2026
Merged

feat/issues-64-67-modules-41-43#185
Darkvader-ship-it merged 1 commit into
PHASE-STELLAR:mainfrom
LamsOfJos:feat/issues-64-67-modules-41-43

Conversation

@LamsOfJos

Copy link
Copy Markdown
Contributor

Summary

Implements issues #64, #65, #66, #67 as isolated, feature-flagged domain modules
following the repo's established pattern (module in lib/*-store.ts + additive
route wiring + UI wiring + node:test suite). All three new flags — phase-136,
phase-137, phase-138 — default off, so every route and UI path is
byte-identical to main until NEXT_PUBLIC_FEATURE_PHASE_136/137/138 (or
FEATURE_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 the
    CID→gateway URL per CID with a TTL (default 5 min, bounded 1 s–24 h).
    recordCidGatewayOutcome({ gateway, ok, latencyMs }) feeds a rolling health
    model (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 cached
    signalMedia: { image, gateway } and an X-Phase136-Gateway header when the
    signal has an IPFS nft_image.
  • app/signals/[id]/page.tsx: renders the resolved gateway URL, falling back
    to 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 bad
wallet were indistinguishable in logs.

  • lib/profile-store.ts: ProfileError carrying code, deterministic
    status, category and retryable; 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, else INTERNAL. toProfileErrorResponse(err) +
    ProfileErrorResponseSchema.
  • app/api/profile/avatar/route.ts: GET/POST catch blocks and POST
    fetch/pin-failure branches emit { error, code, category, retryable } with the
    taxonomy's status when the flag is on; legacy 500 otherwise.
  • components/wallet-avatar.tsx: does one silent retry on a retryable
    upstream 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-memory
    ledger; flag off ⇒ no-op returning 0. BILLABLE_OPERATIONS taxonomy with
    default unit weights, getRequestCost, getCostLedger,
    summarizeCostByOperation (treasury view). Typed CostAttributionError + zod
    schema.
  • app/api/profile/follow/route.ts: suggestions path books
    follow.suggestions + horizon.* + per-profile profile.enrichment cost and
    returns costUnits + X-Phase138-Cost-Units; follow/unfollow POST books
    follow.write (+ notification.create) and returns costUnits.
  • app/profile/[wallet]/follow-button.tsx: FollowSuggestions surfaces the
    request cost as STELLAR GRAPH · <n>u.
  • Bug fix (in scope): the route used isFeatureEnabled,
    FollowSuggestionQuerySchema and getFollowSuggestions without importing
    them — the suggestions endpoint threw ReferenceError at runtime. Imports
    added.

Verification

  • npx tsc --noEmit: 65 → 43 pre-existing errors (net −22, from the
    follow-route missing imports); no new type errors in any changed file.
  • Full test suite: 192 → 217 passing, +25 new tests, 0 regressions (the
    6 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.md for the full breakdown.

Closes #64
Closes #65
Closes #66
Closes #67

@drips-wave

drips-wave Bot commented Aug 31, 2026

Copy link
Copy Markdown

@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! 🚀

Learn more about application limits

@Darkvader-ship-it
Darkvader-ship-it merged commit 3c23684 into PHASE-STELLAR:main Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment