Skip to content

feat(daemon): standalone checkpoint loop + dashboard rework (#505)#511

Merged
oaksprout merged 2 commits into
nextfrom
claude/vigilant-proskuriakova-5faeb0
May 22, 2026
Merged

feat(daemon): standalone checkpoint loop + dashboard rework (#505)#511
oaksprout merged 2 commits into
nextfrom
claude/vigilant-proskuriakova-5faeb0

Conversation

@oaksprout
Copy link
Copy Markdown
Collaborator

Run-mode

FEATURE

Context

Closes #505 — operator nodes on realistic cadence (≥1 novel solution per livenessPeriod) were silently failing every L2 staking checkpoint because the activity window stayed open indefinitely. The existing RewardClaimLoop only fires checkpointAndClaim when calculateStakingReward > 0, but calculateStakingReward returns 0 when liveness is already failing — chicken-and-egg until manual intervention or eviction.

Same session surfaced a SQLite NOT NULL constraint that was mis-flagging successful deliveries as FAILED in the dashboard (on-chain deliveries succeeded, local store insert reverted), plus a chain of dashboard cleanups around the Activity table and Wallet card that operators asked for live during the test session.

Summary of changes

Daemon — new CheckpointLoop (client/src/daemon/checkpoint-loop.ts)

  • Fires bare checkpoint() on each unique staked proxy at livenessPeriod cadence.
  • Config: checkpointIntervalMs (default 300_000), env JINN_CHECKPOINT_INTERVAL_MS.
  • Gated by stakingMode === 'standard'. Master EOA signs (permissionless call, no Safe execTransaction).
  • Idempotent and per-call error-isolated so one bad proxy doesn't stop the rest.

Store — artifacts.desired_state_id NOT NULL fix (client/src/store/store.ts)

  • Legacy DBs predating Task-native IDs ([#220] PR 4: real tJINN earned dashboard #406) defined desired_state_id as NOT NULL. Newer code only writes task_id, which made post-success delivery writes revert with NOT NULL constraint failed: artifacts.desired_state_id, mis-flagging the dashboard row as FAILED.
  • SQLite can't ALTER COLUMN to drop the constraint, so Store now detects the legacy column at startup (hasLegacyDesiredStateId) and mirrors task_id into it on insert.

Dashboard — Activity table (ActivityCard.tsx)

  • Swap Run / Task column order (Run first).
  • Rename state badge pendingactive.
  • Remove the 50-row cap + 360px ScrollArea clip so all rows are visible.
  • Filter "never engaged" FAILED rows: runStartedAt === null OR failureReason matches "not ready" / "not enabled". Catches expired evaluator opportunities and harness-not-enabled rows; keeps real failures visible.
  • Single-SolverNet operators see all rows regardless of manifestCid — previously the delivery manifest CID didn't match the SolverNet manifest CID, silently dropping every COMPLETE row.

Dashboard — Wallet card (WalletCard.tsx, Overview.tsx)

  • Wire the existing rewards.claimedStakingRewardsWei field through to the CLAIMED widget (was reading a non-existent claimedJinnLifetime and defaulting to 0).
  • Add a Service field (e.g. #50) under Identity alongside Agent / Master / Safe, so operators can quote their on-chain service id without digging through jinn status.

Tests

  • New CheckpointLoop suite (6 tests): per-proxy dedup, skip non-staked services, per-call error isolation, intervalMs=0 no-op.
  • ActivityCard.test.tsx updated for active label + new filter behavior (both "never engaged" signatures + a real-failure keep case).
  • WalletCard.test.tsx fixture updated for the new serviceId field on ServiceIdentity.

Acceptance

  • CheckpointLoop ticks every livenessPeriod and is gated by config.
  • Loop survives one bad proxy without stopping subsequent calls.
  • Loop is a no-op when intervalMs = 0.
  • Successful deliveries on legacy-schema DBs no longer mis-flag as FAILED.
  • Activity table shows all qualifying rows (no clip), with Run column first and active for in-flight state.
  • Evaluator-not-enabled and "never started" FAILED rows are filtered; real solver failures remain visible.
  • Wallet card CLAIMED widget reflects the daemon's claimedStakingRewardsWei total; Identity panel shows on-chain Service #id.
  • vitest run clean for checkpoint-loop, ActivityCard, WalletCard suites.

Out of scope

  • Replacing the L2 stOLAS (≡ L2 JINN) → L1 tJINN bridge readout: the dashboard's CLAIMABLE / CLAIMED widgets still surface L2 reward queue values rather than real L1 distributor balance. Honest fix needs a JinnDistributor read or an L1-balance widget rename.
  • Evaluator role activation flow surfaced by the new filter: the filter masks the symptom (FAILED rows), but the underlying "operator joined evaluator without jinn harnesses enable swe-rebench-v2-evaluator" UX gap remains.
  • Resolving the nonce race between RewardClaimLoop and CheckpointLoop on the master EOA — the failure is non-fatal (loop logs and retries on next tick) but warrants a follow-up to coordinate transactions or move the checkpoint call onto the Safe.

Reference

🤖 Generated with Claude Code

oaksprout and others added 2 commits May 22, 2026 14:43
Daemon — new CheckpointLoop (issue #505)

OLAS-style staking requires someone to call checkpoint() on the proxy to
advance tsCheckpoint. The existing reward-claim loop only calls
checkpointAndClaim when calculateStakingReward > 0; if liveness is
already failing because the activity window is too wide, that gate
never opens and the cycle stays stuck until eviction. The new
CheckpointLoop fires bare checkpoint() on each unique staked proxy at
livenessPeriod cadence (default 300_000 ms, env
JINN_CHECKPOINT_INTERVAL_MS), independent of pending reward. Master EOA
signs; checkpoint() is permissionless so no Safe execTransaction.

Store — SQLite NOT NULL fix on artifacts.desired_state_id

Legacy DBs predating Task-native IDs defined desired_state_id as NOT
NULL. Newer insertArtifact only writes task_id, which made post-success
delivery writes revert with "NOT NULL constraint failed:
artifacts.desired_state_id" — and mis-flagged completed deliveries as
FAILED in the dashboard while the on-chain side was fine. SQLite can't
ALTER COLUMN to drop the constraint, so Store now detects the legacy
column at startup and mirrors task_id into it on insert.

Dashboard — Activity table

- Swap Run / Task column order (Run first).
- Rename state badge "pending" → "active".
- Remove the 50-row + 360px scroll clip so all rows are visible.
- Filter "never engaged" FAILED rows: runStartedAt=null OR failureReason
  matches "not ready" / "not enabled". Catches expired evaluator
  opportunities and harness-not-enabled rows; keeps real failures
  (interrupted runs, real impl errors) visible.
- Single-SolverNet operators see all rows regardless of manifestCid —
  previously the delivery manifest CID didn't match the SolverNet
  manifest CID, silently dropping COMPLETE rows.

Dashboard — Wallet card

- Wire the existing claimedStakingRewardsWei field from /v1/status into
  the CLAIMED widget; was reading a non-existent field name and
  defaulting to '0'.
- Add a Service field (e.g. #50) under Identity alongside Agent /
  Master / Safe, so operators can quote their on-chain service id
  without digging through `jinn status`.

Tests

- 6-test CheckpointLoop suite (per-proxy dedup, skip-not-staked,
  per-call error isolation, intervalMs=0 no-op).
- ActivityCard tests cover the new filter (both "never engaged"
  signatures + a real-failure keep case) and the active-badge label.
- WalletCard service-identity fixture updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…uriakova-5faeb0

# Conflicts:
#	client/src/dashboard/spa/src/pages/Overview.tsx
@oaksprout oaksprout merged commit 65de316 into next May 22, 2026
5 of 7 checks passed
@ritsuKai2000 ritsuKai2000 deleted the claude/vigilant-proskuriakova-5faeb0 branch May 22, 2026 15:23
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.

feat(daemon): standalone checkpoint loop to keep staking liveness window short

1 participant