Skip to content

Fix: 4 issues (#99, #100, #101, #102) - #129

Merged
victorisiguzoruzoma874 merged 2 commits into
SynapsVault:mainfrom
197827:simple/99-100-101-102
Oct 18, 2026
Merged

victorisiguzoruzoma874 merged 2 commits into
SynapsVault:mainfrom
197827:simple/99-100-101-102

Conversation

@197827

@197827 197827 commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Closes #99
Closes #100
Closes #101
Closes #102

Issues

#99 /agent/status reports earnings as verification count times list price

Where

src/routes/verify.ts (totalEarned = totalVerifications * pricePerVerification).

Problem

The figure includes rejected or unsettled calls and ignores price changes.

Expected

Sum the amounts actually recorded for settled verification payments.

Acceptance criteria

  • A test with mixed outcomes reports only settled amounts.

#100 Publisher analytics loads all payments and filters them in JavaScript

Where

src/routes/publishers.ts (GET /publishers/me/analytics).

Problem

Each dashboard load reads every payment for the publisher's resources and computes totals in memory.

Expected

Aggregate per resource with GROUP BY in SQL, and paginate the recent payments.

Acceptance criteria

  • Totals come from one aggregate query.
  • Totals match the previous implementation in a test.

#101 Event listener advances the checkpoint even when a handler fails

Where

src/workers/eventListener.ts (pollEvents: handler errors are caught, then saveLastLedger(maxLedger + 1) still runs).

Problem

A transient database error permanently skips that on-chain event.

Expected

Stop the checkpoint before the first failed event, or record failed events for retry.

Acceptance criteria

  • A test where a handler throws leaves the checkpoint before that event.
  • The failed event is picked up on the next poll.

#102 Event polls can overlap when one poll runs longer than the interval

Where

src/workers/eventListener.ts (setInterval(pollEvents, 30_000) with no in-flight guard).

Problem

Two polls can read the same ledger range and process events twice. Checkpoint writes can go backwards.

Expected

Add an in-flight guard and write the checkpoint only forward.

Acceptance criteria

  • A test with a slow poll runs at most one poll at a time.

Summary

For #99, the settled filter depends on how settlement is tracked; if no settled column exists yet, add one to payments (see schema step) and backfill existing rows as settled. For #100, keep the response shape identical so the existing test's toMatchObject assertions still pass; the aggregate query must filter by recipientAddress = publisher.walletAddress to preserve the current semantics. For #101/#102, the in-flight guard and monotonic checkpoint write are independent of the failed-event retry table; the retry table is optional if the simpler 'stop before first failed event' approach is chosen, but the schema step adds it for completeness. Tests for the event listener should mock the stellar-sdk RPC server and the ledger tracker file path (via LEDGER_TRACKER_PATH env) to avoid touching disk.

Changes

  • In the GET /agent/status handler, replace totalEarned = totalVerifications * pricePerVerification with a SQL aggregate that sums the amounts actually recorded for settled verification payments. Query the payments table joined to verifications (or filter by the settled status/flag on the payments/verifications rows) for records belonging to the agent's wallet (config.PAY_TO / recipientAddress), and use sum(payments.amount) (cast to numeric) to compute totalEarned. Keep the existing response shape (totalEarned.toFixed(4)). Import sum, sql from drizzle-orm and payments from the schema. Do not count rejected or unsettled verifications, and do not use the current list price. Update the test file to add a case with mixed outcomes (settled, rejected, unsettled) asserting only settled amounts are summed; extend the db mock to handle the aggregate query. (src/routes/verify.ts)
  • Add a test to GET /agent/status coverage that seeds verification/payment rows with mixed outcomes (settled, rejected, unsettled) and asserts stats.totalEarned equals the sum of only the settled payment amounts (not count × price). Update the db mock so the new aggregate query path (sum over payments for settled verifications) is supported alongside the existing verifications/resources selects. (src/routes/verify.agent-status.test.ts)
  • Rewrite GET /publishers/me/analytics to aggregate in SQL: replace the in-memory allPayments.filter(...) logic with a single query using GROUP BY payments.resourceId (joined to the publisher's resources) that computes count(*) and sum(amount) per resource, filtered by recipientAddress = publisher.walletAddress. Compute the summary totals from the same aggregate result (or a second aggregate over the same filter). For recentPayments, run a separate paginated query per resource (or a windowed query) using ORDER BY paid_at DESC LIMIT 5 OFFSET 0 instead of slicing in memory. Preserve the existing response shape (totalEarned as .toFixed(4), totalSales, recentPayments fields). Import sum, count, sql, desc from drizzle-orm. (src/routes/publishers.ts)
  • Update the analytics tests to cover the new SQL aggregation path: extend the db mock so the GROUP BY aggregate query and the paginated recent-payments query are supported, and add a test asserting the new totals (per-resource totalEarned/totalSales and summary totals) match the previous in-memory implementation's output for the same fixture data. (src/routes/publishers.test.ts)
  • Fix the event listener checkpoint handling for both Event listener advances the checkpoint even when a handler fails #101 and Event polls can overlap when one poll runs longer than the interval #102. (a) In pollEvents, track lastSuccessfulLedger starting at lastLedger; iterate events in ledger order and only advance lastSuccessfulLedger after each event's handler completes successfully. On a handler error, break out of the loop (do not advance past the failed event) and log it; then call saveLastLedger(lastSuccessfulLedger + 1) only if it is greater than the currently stored checkpoint. This ensures a failed event is retried on the next poll. (b) Add a module-level let pollInFlight = false guard at the top of pollEvents: if already true, return immediately; set it true in a try/finally that resets it. (c) Make saveLastLedger monotonic: read the current stored value and only write if the new ledger is greater than the stored one. Add tests: one where a handler throws and asserts the checkpoint stays before that event and the event is processed on the next poll; one with a deliberately slow poll asserting at most one poll runs concurrently. (src/workers/eventListener.ts)
  • Add a failed_events table (id, eventId, ledger, topic, payload, error, attempts, createdAt) to support recording failed events for retry, and add a settled boolean (or status enum) column to the payments table so the agent-status earnings query can filter to settled payments only. Add an index on payments.resourceId and payments.recipientAddress to support the analytics GROUP BY and agent-status sum queries. (src/db/schema.ts)

Co-authored-by: Simple Agent <agent@simple.dev>
@vercel

vercel Bot commented Oct 18, 2026

Copy link
Copy Markdown

@197827 is attempting to deploy a commit to the work Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Oct 18, 2026

Copy link
Copy Markdown

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

- src/db/schema.ts: merged [markers] — model resolved the conflict markers
- src/workers/eventListener.ts: merged [markers] — model resolved the conflict markers

Co-authored-by: Simple Agent <agent@simple.dev>
@victorisiguzoruzoma874
victorisiguzoruzoma874 merged commit a9964ed into SynapsVault:main Oct 18, 2026
0 of 2 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

2 participants