Commit graph

670 commits

Author SHA1 Message Date
johba
e168b2cc4e Merge pull request 'fix: feat: RPC-only staking helpers for red-team agent (#518)' (#523) from fix/issue-518 into master 2026-03-09 04:12:55 +01:00
openhands
722ecaaa0e fix: address review findings in stake-rpc.ts (#518)
- Remove dead krkAddress field from UnstakeRpcConfig (bug)
- Drop swap.js import to avoid transitive Playwright dependency; fix
  header comment to accurately describe the module boundary (warning)
- Inline pollReceipt() returning TxReceipt so snatch receipt is reused
  for log parsing without a second round-trip (warning)
- Use ZeroAddress from ethers instead of manual constant (info)
- Add comment on fromBlock '0x0' genesis-scan caveat (info)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 02:48:51 +00:00
openhands
1af022624f ci: retrigger after infra failure 2026-03-09 02:17:50 +00:00
openhands
fd44fa0bcf fix: feat: RPC-only staking helpers for red-team agent (#518)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 02:06:41 +00:00
johba
3083e24eab Merge pull request 'fix: feat: Anvil snapshot/revert and ethPerToken helpers (#519)' (#521) from fix/issue-519 into master 2026-03-09 02:53:04 +01:00
openhands
e01ef23560 fix: feat: Anvil snapshot/revert and ethPerToken helpers (#519)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 01:23:36 +00:00
openhands
866474510b fix: feat: Anvil snapshot/revert and ethPerToken helpers (#519)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 00:53:17 +00:00
openhands
b2db3c7ae5 fix: feat: Anvil snapshot/revert and ethPerToken helpers (#519)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 00:12:49 +00:00
johba
83e9a52b5b Merge pull request 'fix: Issue #447 remains unresolved: no caching path exists for POST GraphQL requests (#478)' (#516) from fix/issue-478 into master 2026-03-06 21:14:17 +01:00
openhands
694a68ad27 fix: Issue #447 remains unresolved: no caching path exists for POST GraphQL requests (#478)
Address AI review findings:

- Bug: restore 30 s periodic eviction via setInterval so queries that
  are never repeated don't accumulate forever (add setInterval/
  clearInterval to ESLint globals to allow it)
- Bug: fix .finally() race – use identity check before deleting the
  in-flight key so a waiting request's replacement promise is never
  evicted by the original promise's cleanup handler
- Warning: replace `new URL(c.req.url).search` with a string-split
  approach that cannot throw on relative URLs
- Warning: add MAX_CACHE_ENTRIES (500) cap with LRU-oldest eviction to
  bound memory growth from callers with many unique variable sets
- Warning: prefix cache key with c.req.path so /graphql and / can
  never produce cross-route cache collisions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 19:49:13 +00:00
openhands
ad19d8fc00 fix: Issue #447 remains unresolved: no caching path exists for POST GraphQL requests (#478)
Replace setInterval-based eviction with lazy eviction to avoid the
no-undef ESLint error (setInterval is not in the allowed globals list).
Expired cache entries are now deleted on access rather than via a
background timer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 19:11:21 +00:00
openhands
032222fac9 fix: Issue #447 remains unresolved: no caching path exists for POST GraphQL requests (#478)
Add server-side response cache + in-flight coalescing to Ponder's Hono
API layer (services/ponder/src/api/index.ts).

Previously every polling client generated an independent DB query, giving
O(users × 1/poll_interval) load. With a 5 s in-process cache keyed on the
raw request body (POST) or query string (GET), the effective DB hit rate
is capped at O(1/5s) regardless of how many clients are polling.

In-flight coalescing ensures that N concurrent identical queries that
arrive before the first response is ready all share a single DB hit
instead of each issuing their own. Expired entries are evicted every 30 s
to keep memory use bounded.

The 5 s TTL deliberately matches the existing Caddy `Cache-Control:
public, max-age=5` header so that if a caching proxy/CDN is layered in
front later, both layers stay in sync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 18:56:48 +00:00
johba
b97ab18514 Merge pull request 'fix: unstakePosition() has no error handling at all (#482)' (#514) from fix/issue-482 into master 2026-03-06 19:45:01 +01:00
openhands
98dd839bb5 fix: unstakePosition() has no error handling at all (#482)
Address AI reviewer warnings:
- Add separate unstakeError ref so unstake failures are not displayed
  via the load-data error UI whose Retry calls loadActivePositionData
- Add exitSucceeded flag to distinguish pre-exit failures ("Failed to
  unstake position.") from post-exit data-refresh failures ("Position
  unstaked, but failed to refresh data."), preventing factually wrong
  messages after a successful transaction
- Unstake error UI has no Retry button; the Unstake button in actions
  remains available for retrying a failed transaction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 18:15:30 +00:00
openhands
037de2d1fd ci: retrigger after infra failure 2026-03-06 17:44:30 +00:00
openhands
a8defd10b4 fix: unstakePosition() has no error handling at all (#482)
Wrap the entire unstakePosition() body in try/catch/finally so that
errors from exitPosition(), loadActivePositions(), and loadPositions()
are caught, displayed to the user via the existing error ref, and
loading state is always reset correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 17:32:02 +00:00
johba
c45a898e90 Merge pull request 'fix: Stale values shown during retry after prior successful load (#489)' (#510) from fix/issue-489 into master 2026-03-06 18:25:55 +01:00
openhands
4eea19bf53 fix: Stale values shown during retry after prior successful load (#489) 2026-03-06 16:51:22 +00:00
johba
70f3336024 Merge pull request 'fix: networkidle still used in stake.ts login flow (#501)' (#508) from fix/issue-501 into master 2026-03-06 17:46:13 +01:00
openhands
962b126e8b fix: networkidle still used in stake.ts login flow (#501)
Replace waitForLoadState('networkidle') in the post-login redirect with
waitForURL('**/app/stake**'). Persistent WebSocket connections prevent
networkidle from ever firing, mirroring the same fix applied to navigate.ts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 16:11:24 +00:00
johba
1302feef82 Merge pull request 'fix: P&L percentage line understates tax cost when taxDue > 0 (#504)' (#505) from fix/issue-504 into master 2026-03-06 17:02:17 +01:00
openhands
e4ed1474e1 fix: P&L percentage line understates tax cost when taxDue > 0 (#504)
Include taxDue in taxCostPercent computation so the Tax% and Net%
shown in the header P&L line are consistent with the Tax Cost card
and Total row, which already use taxDue + taxPaid.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 15:32:21 +00:00
johba
b2756e8c99 Merge pull request 'fix: Misleading 'Tax Paid' label — value includes unpaid tax due (#374)' (#503) from fix/issue-374 into master 2026-03-06 13:14:51 +01:00
openhands
18a2d4138e fix: Misleading 'Tax Paid' label — value includes unpaid tax due (#374)
taxPaidGes = taxDue + taxPaid, so the displayed value includes both
outstanding tax and historically paid tax. Rename the UI label from
'Tax Paid' to 'Tax Cost' to accurately reflect the combined amount.
Update the matching E2E test selector accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 11:51:45 +00:00
johba
98dac2f67e Merge pull request 'fix: wait_healthy does not fail fast when a service exits or crashes during the health-check window (#387)' (#502) from fix/issue-387 into master 2026-03-06 12:44:33 +01:00
openhands
c943db379f fix: wait_healthy does not fail fast when a service exits or crashes during the health-check window (#387) 2026-03-06 11:20:54 +00:00
johba
4daabe335d Merge pull request 'fix: health-checks.ts reads deployments-local.json directly, bypassing all env var overrides (#412)' (#500) from fix/issue-412 into master 2026-03-06 12:12:42 +01:00
openhands
ab57c5bccd fix: navigateSPA waitForLoadState networkidle times out due to persistent WebSocket connections
Replace waitForLoadState('networkidle') with a comment explaining that callers
must assert on a route-specific element. The SPA keeps persistent WebSocket
connections for blockchain event subscriptions, so networkidle never fires
within the 10 s window, causing spurious TimeoutErrors in 01-acquire-and-stake
and 02-max-stake-all-tax-rates.

Vue Router processes popstate synchronously inside page.evaluate(), so the
route transition has already started by the time evaluate() resolves. Callers'
toBeVisible() assertions (with their own timeouts) serve as the readiness gate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 10:33:04 +00:00
johba
a937f1cb4c docs: scope engineering principles to infra/tests, not frontend polling (#470)
Clarifies that the event-driven engineering principles apply to infrastructure (Docker, scripts, startup/teardown) and test/scenario execution — NOT frontend HTTP API polling.

Frontend polling (e.g. LiveStats → Ponder GraphQL every 30s) is fine. The scalability solution is caching at the proxy layer (`Cache-Control` headers via Caddy), not WebSocket subscriptions.

Relates to #447

Co-authored-by: openhands <openhands@all-hands.dev>
Reviewed-on: https://codeberg.org/johba/harb/pulls/470
2026-03-06 11:17:50 +01:00
openhands
97a4ef7cd3 fix: health-checks.ts reads deployments-local.json directly, bypassing all env var overrides (#412)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 10:12:28 +00:00
johba
a6c238570f Merge pull request 'fix: Swap completion detection relies on a fragile timing assumption (#415)' (#499) from fix/issue-415 into master 2026-03-06 10:55:54 +01:00
openhands
c0695e101f fix: Swap completion detection relies on a fragile timing assumption (#415)
Replace the try/catch on observing the transient 'Submitting…' button text with a
two-phase disabled→enabled check. The button gains the `disabled` attribute the
moment `swapping=true` and loses it when the swap finishes. By placing
`toBeEnabled({ timeout: 60_000 })` unconditionally after the try block, both
paths (fast RPC where disabled state cycles in <100 ms and slow RPC where it is
clearly observable) now wait for the actual ready state rather than falling
through to only a 2-second static guard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 09:27:17 +00:00
johba
5bea99ddf4 Merge pull request 'fix: Dozens of bare waitForTimeout() calls remain throughout e2e tests (#418)' (#496) from fix/issue-418 into master 2026-03-06 10:15:18 +01:00
openhands
3507538fce fix: Dozens of bare waitForTimeout() calls remain throughout e2e tests (#418)
Replace all 10 bare waitForTimeout() calls in tests/e2e/usertest/helpers.ts
with proper event-driven Playwright waits per AGENTS.md Engineering Principle #3
("replace when touched"):

connectWallet():
- Remove 500ms resize-event sleep: connectButton.isVisible({ timeout: 5_000 })
  already waits for the layout to settle after the resize event
- Remove 2000ms "connector init" sleep: the subsequent isVisible check was
  already event-driven
- Replace 2x 1000ms panel-animation sleeps with
  injectedConnector.waitFor({ state: 'visible' }) — the .connectors-element
  appearing is the exact observable DOM event
- Remove 2x 2000ms "handshake" sleeps: walletDisplay.waitFor() at the end of
  the function is the correct gate for connection completion

attemptStake():
- Remove 3000ms post-goto sleep: tokenAmountSlider.waitFor() at the next line
  is the correct page-load gate
- Remove 2x 500ms debounce sleeps after fill/select: stakeButton.waitFor()
  downstream is the correct reactive-state gate
- Remove 3000ms post-transaction sleep: the button returning to "Stake" text
  (waitFor at line 619) is already the correct transaction-completion gate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 08:47:22 +00:00
openhands
92fa38e328 fix: Dozens of bare waitForTimeout() calls remain throughout e2e tests (#418)
Add eslint-disable-next-line no-restricted-syntax comments with
justification to the 10 bare waitForTimeout() calls in
tests/e2e/usertest/helpers.ts.

All waitForTimeout calls in the spec files (01–06 and all usertest
specs) were already properly documented after PR #417. helpers.ts was
the only remaining file with bare calls:
- 6 in connectWallet(): wallet connector panel animation and
  connection handshake have no observable DOM event to await
- 4 in attemptStake(): Ponder indexing lag, debounced form handlers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 08:15:48 +00:00
johba
524a6a66d9 Merge pull request 'fix: webapp-entrypoint.sh CI path bypasses contracts.env sourcing without documentation (#422)' (#491) from fix/issue-422 into master 2026-03-06 09:03:38 +01:00
openhands
34dbd48654 ci: retrigger after infra failure 2026-03-06 07:34:50 +00:00
openhands
6c54a92657 fix: webapp-entrypoint.sh CI path bypasses contracts.env sourcing without documentation (#422)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 07:24:54 +00:00
openhands
d301f75523 fix: webapp-entrypoint.sh CI path bypasses contracts.env sourcing without documentation (#422)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 07:01:07 +00:00
johba
8c893d68e1 Merge pull request 'fix: total computed has no undefined guard — shows initial stake amount during loading (#424)' (#488) from fix/issue-424 into master 2026-03-06 07:54:19 +01:00
openhands
0063e94007 fix: \total\ computed has no undefined guard — shows initial stake amount during loading (#424)
When profit or taxPaidGes were undefined (data still loading), the
computed returned props.amount due to the ?? 0 fallbacks. The computed
now returns undefined until both values are loaded, and the template
guard is simplified to total !== undefined.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 06:31:49 +00:00
johba
37d7339fbf Merge pull request 'fix: commaNumber silently returns '0' for NaN and falsy values (#427)' (#485) from fix/issue-427 into master 2026-03-06 07:20:27 +01:00
openhands
04fbca939f fix: \commaNumber\ silently returns '0' for NaN and falsy values (#427)
Replace truthiness guard with Number.isFinite() so NaN and Infinity are
explicitly rejected rather than silently masked. Zero is now handled by
toLocaleString, which returns '0' correctly. Add test cases for NaN and
Infinity.
2026-03-06 05:51:50 +00:00
johba
67c0ed953a Merge pull request 'fix: getErrorMessage silently ignores viem's shortMessage (#430)' (#484) from fix/issue-430 into master 2026-03-06 06:41:25 +01:00
openhands
fc008fb9c1 fix: correct TypeScript cast for shortMessage in getErrorMessage (#430)
The direct cast of Error to Record<string, unknown> is rejected by
TypeScript because the types don't sufficiently overlap. Cast via
unknown first to satisfy the compiler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 05:13:27 +00:00
openhands
a8a5d1ab93 fix: getErrorMessage silently ignores viem's shortMessage (#430)
viem's BaseError extends Error, so the instanceof Error branch was
firing and returning error.message before the isRecord branch could
check shortMessage. Check shortMessage first inside instanceof Error
so terse viem messages are shown to users instead of verbose full ones.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 05:02:10 +00:00
johba
d9329d70b3 Merge pull request 'fix: CollapseActive.vue: fixed 5-second delay in unstakePosition() should be replaced with polling (#448)' (#480) from fix/issue-448 into master 2026-03-06 05:52:04 +01:00
openhands
4aee33ad74 ci: retrigger after infra failure 2026-03-06 04:29:45 +00:00
openhands
372f49f6a5 fix: wrap unstakePosition polling loop in try/finally to prevent loading lock (#448)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 04:18:52 +00:00
openhands
e85ff85950 fix: CollapseActive.vue: fixed 5-second delay in unstakePosition() should be replaced with polling (#448)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 03:44:04 +00:00