# Explorer Freshness And Diagnostics Contract

This document defines the minimum public freshness and diagnostics payloads the SolaceScan frontend needs in order to present chain activity, transaction visibility, and snapshot posture without relying on frontend inference.

It is intended to close the remaining gap between:

- a frontend that now renders and explains state honestly, and
- upstream APIs that still omit authoritative freshness metadata for several critical surfaces.

## Goal

The frontend should be able to answer these questions directly from public API fields:

1. Is the chain head current?
2. When was the latest visible transaction indexed?
3. What is the latest non-empty block?
4. Is the homepage using a live feed, a snapshot, or mixed evidence?
5. Which subsystem is stale: RPC, indexing, relay monitoring, or stats?
6. Which values are reported directly vs inferred vs unavailable?

The frontend should not have to infer these from a combination of:

- `/api/v2/stats`
- `/api/v2/main-page/blocks`
- `/api/v2/main-page/transactions`
- `/explorer-api/v1/track1/bridge/status`

unless there is no backend alternative.

## Design Principles

- Prefer explicit freshness fields over derived heuristics.
- Separate chain freshness from indexed-transaction freshness.
- Distinguish reported facts from inferred or partial facts.
- Make incompleteness first-class.
- Keep the contract calm and operational, not alarmist.

## Proposed Public Endpoints

Two additions are recommended.

### 1. Extend `GET /api/v2/stats`

This endpoint already feeds the homepage summary cards. It should become the authoritative public summary for chain freshness and indexed activity freshness.

### 2. Extend `GET /explorer-api/v1/track1/bridge/status`

This endpoint already powers Mission Control. It should expose snapshot/feed posture and subsystem freshness more directly.

If backend implementation prefers separation, these fields may instead be exposed from a new endpoint:

`GET /explorer-api/v1/track1/observability/freshness`

The frontend does not require a separate endpoint as long as the fields below are available from a stable public contract.

## Required Additions To `/api/v2/stats`

### Current gaps

The current `stats` payload gives totals, but it does not reliably expose:

- latest indexed transaction timestamp
- latest non-empty block
- authoritative utilization freshness
- confidence/completeness metadata

### Required fields

```json
{
  "total_blocks": 3873353,
  "total_transactions": 52391,
  "total_addresses": 10294,
  "latest_block": 3873353,
  "average_block_time": 2000,
  "gas_prices": {
    "slow": 0.02,
    "average": 0.03,
    "fast": 0.05
  },
  "network_utilization_percentage": 0,
  "transactions_today": 18,

  "freshness": {
    "chain_head": {
      "block_number": 3873353,
      "timestamp": "2026-04-10T21:42:15Z",
      "age_seconds": 1,
      "source": "reported",
      "confidence": "high"
    },
    "latest_indexed_transaction": {
      "hash": "0x...",
      "block_number": 3858013,
      "timestamp": "2026-04-10T12:31:05Z",
      "age_seconds": 33070,
      "source": "reported",
      "confidence": "high"
    },
    "latest_non_empty_block": {
      "block_number": 3858013,
      "timestamp": "2026-04-10T12:31:05Z",
      "age_seconds": 33070,
      "distance_from_head": 15340,
      "source": "reported",
      "confidence": "high"
    },
    "latest_indexed_block": {
      "block_number": 3873353,
      "timestamp": "2026-04-10T21:42:15Z",
      "age_seconds": 1,
      "source": "reported",
      "confidence": "high"
    }
  },

  "completeness": {
    "transactions_feed": "complete",
    "blocks_feed": "complete",
    "gas_metrics": "partial",
    "utilization_metrics": "partial"
  },

  "sampling": {
    "stats_generated_at": "2026-04-10T21:42:16Z",
    "stats_window_seconds": 300,
    "rpc_probe_at": "2026-04-10T21:42:15Z"
  }
}
```

## Field Semantics

### `freshness.chain_head`

The latest chain head known from the authoritative public RPC or canonical head source.

This is the answer to:

- "Is the chain alive?"
- "Is head visibility current?"

### `freshness.latest_indexed_transaction`

The most recent transaction currently visible in the public indexed transaction feed.

This is the answer to:

- "How recent is the latest visible transaction?"

### `freshness.latest_non_empty_block`

The most recent indexed block containing one or more transactions.

This is the answer to:

- "Are head blocks empty because the chain is quiet?"
- "What is the last block with visible activity?"

### `freshness.latest_indexed_block`

The latest block successfully indexed into the explorer's public block dataset.

This disambiguates:

- current chain head
- current explorer indexed head

### `completeness.*`

Simple public-facing availability states for each summary subsystem:

- `complete`
- `partial`
- `stale`
- `unavailable`

These should not be interpreted as outage severity; they describe data completeness only.

### `sampling.*`

Metadata for when the summary itself was generated and what freshness window it represents.

## Required Additions To Mission Control Payload

Mission Control currently provides useful relay detail, but the homepage still infers snapshot scope and partial feed posture from surrounding evidence.

### Required fields

```json
{
  "data": {
    "status": "degraded",
    "checked_at": "2026-04-10T21:42:16Z",
    "mode": {
      "kind": "snapshot",
      "updated_at": "2026-04-10T21:42:16Z",
      "age_seconds": 1,
      "reason": "live_homepage_stream_not_attached",
      "scope": "relay_monitoring_homepage_card_only",
      "source": "reported",
      "confidence": "high"
    },
    "subsystems": {
      "rpc_head": {
        "status": "operational",
        "updated_at": "2026-04-10T21:42:15Z",
        "age_seconds": 1,
        "source": "reported",
        "confidence": "high"
      },
      "tx_index": {
        "status": "stale",
        "updated_at": "2026-04-10T12:31:05Z",
        "age_seconds": 33070,
        "source": "reported",
        "confidence": "high"
      },
      "bridge_relay_monitoring": {
        "status": "degraded",
        "updated_at": "2026-04-10T21:42:16Z",
        "age_seconds": 1,
        "source": "reported",
        "confidence": "high"
      },
      "stats_summary": {
        "status": "partial",
        "updated_at": "2026-04-10T21:42:16Z",
        "age_seconds": 1,
        "source": "reported",
        "confidence": "medium"
      }
    }
  }
}
```

## Required Enumerations

These enums should be consistent across public surfaces.

### Activity interpretation

- `active`
- `quiet`
- `sparse_activity`
- `fresh_head_stale_tx_visibility`
- `limited_observability`

This value should be emitted only when the backend can support it directly. Otherwise the frontend may continue to derive it as a presentation layer.

### Data source confidence

- `high`
- `medium`
- `low`
- `unknown`

### Data origin

- `reported`
- `inferred`
- `sampled`
- `unavailable`

### Completeness

- `complete`
- `partial`
- `stale`
- `unavailable`

## Example Payloads

These examples are intended to accelerate frontend/backend alignment by showing how the contract should represent common live states.

### Example A: Healthy Live State

```json
{
  "freshness": {
    "chain_head": {
      "block_number": 3874000,
      "timestamp": "2026-04-10T22:10:14Z",
      "age_seconds": 1,
      "source": "reported",
      "confidence": "high"
    },
    "latest_indexed_block": {
      "block_number": 3874000,
      "timestamp": "2026-04-10T22:10:14Z",
      "age_seconds": 1,
      "source": "reported",
      "confidence": "high"
    },
    "latest_indexed_transaction": {
      "hash": "0x...",
      "block_number": 3873998,
      "timestamp": "2026-04-10T22:10:10Z",
      "age_seconds": 5,
      "source": "reported",
      "confidence": "high"
    },
    "latest_non_empty_block": {
      "block_number": 3873998,
      "timestamp": "2026-04-10T22:10:10Z",
      "age_seconds": 5,
      "distance_from_head": 2,
      "source": "reported",
      "confidence": "high"
    }
  },
  "completeness": {
    "transactions_feed": "complete",
    "blocks_feed": "complete",
    "gas_metrics": "complete",
    "utilization_metrics": "complete"
  }
}
```

### Example B: Quiet Chain But Current

```json
{
  "freshness": {
    "chain_head": {
      "block_number": 3875000,
      "timestamp": "2026-04-10T23:10:14Z",
      "age_seconds": 1,
      "source": "reported",
      "confidence": "high"
    },
    "latest_indexed_block": {
      "block_number": 3875000,
      "timestamp": "2026-04-10T23:10:14Z",
      "age_seconds": 1,
      "source": "reported",
      "confidence": "high"
    },
    "latest_indexed_transaction": {
      "hash": "0x...",
      "block_number": 3874902,
      "timestamp": "2026-04-10T23:01:42Z",
      "age_seconds": 512,
      "source": "reported",
      "confidence": "high"
    },
    "latest_non_empty_block": {
      "block_number": 3874902,
      "timestamp": "2026-04-10T23:01:42Z",
      "age_seconds": 512,
      "distance_from_head": 98,
      "source": "reported",
      "confidence": "high"
    }
  },
  "activity_interpretation": "quiet"
}
```

### Example C: Fresh Head, Stale Transaction Visibility

```json
{
  "freshness": {
    "chain_head": {
      "block_number": 3876000,
      "timestamp": "2026-04-11T00:10:14Z",
      "age_seconds": 1,
      "source": "reported",
      "confidence": "high"
    },
    "latest_indexed_block": {
      "block_number": 3875999,
      "timestamp": "2026-04-11T00:10:12Z",
      "age_seconds": 3,
      "source": "reported",
      "confidence": "high"
    },
    "latest_indexed_transaction": {
      "hash": "0x...",
      "block_number": 3860660,
      "timestamp": "2026-04-10T15:02:10Z",
      "age_seconds": 32900,
      "source": "reported",
      "confidence": "high"
    },
    "latest_non_empty_block": {
      "block_number": 3860660,
      "timestamp": "2026-04-10T15:02:10Z",
      "age_seconds": 32900,
      "distance_from_head": 15340,
      "source": "reported",
      "confidence": "high"
    }
  },
  "activity_interpretation": "fresh_head_stale_tx_visibility",
  "completeness": {
    "transactions_feed": "stale",
    "blocks_feed": "complete",
    "gas_metrics": "partial",
    "utilization_metrics": "partial"
  }
}
```

### Example D: Snapshot Mode State

```json
{
  "data": {
    "status": "degraded",
    "checked_at": "2026-04-11T00:10:15Z",
    "mode": {
      "kind": "snapshot",
      "updated_at": "2026-04-11T00:10:15Z",
      "age_seconds": 1,
      "reason": "live_homepage_stream_not_attached",
      "scope": "relay_monitoring_homepage_card_only",
      "source": "reported",
      "confidence": "high"
    },
    "subsystems": {
      "rpc_head": {
        "status": "operational",
        "updated_at": "2026-04-11T00:10:14Z",
        "age_seconds": 1,
        "source": "reported",
        "confidence": "high"
      },
      "tx_index": {
        "status": "stale",
        "updated_at": "2026-04-10T15:02:10Z",
        "age_seconds": 32900,
        "source": "reported",
        "confidence": "high"
      },
      "bridge_relay_monitoring": {
        "status": "degraded",
        "updated_at": "2026-04-11T00:10:15Z",
        "age_seconds": 1,
        "source": "reported",
        "confidence": "high"
      }
    }
  }
}
```

## Frontend Usage Rules

Once the fields above exist, the frontend should follow these rules:

1. Use backend freshness fields directly where present.
2. Stop deriving latest transaction age from the transactions page feed when `freshness.latest_indexed_transaction` is available.
3. Stop deriving last non-empty block from recent block scanning when `freshness.latest_non_empty_block` is available.
4. Use `mode.kind`, `mode.reason`, and `mode.scope` directly for homepage snapshot messaging.
5. Use `source` and `confidence` badges only where they improve trust and do not clutter.

## Backward-Compatible Rollout Plan

### Phase A

Add fields without removing any current keys:

- extend `/api/v2/stats`
- extend bridge status payload with `mode` and `subsystems`

### Phase B

Frontend prefers new fields when available and falls back to inference when absent.

### Phase C

Once fields are consistently present in production:

- reduce frontend inference paths
- remove duplicate explanatory fallback logic where it is no longer needed

## Minimum Viable Backend Implementation

If full rollout is not possible immediately, the minimum high-leverage addition is:

### `/api/v2/stats`

- `freshness.chain_head`
- `freshness.latest_indexed_transaction`
- `freshness.latest_non_empty_block`
- `sampling.stats_generated_at`

### `/explorer-api/v1/track1/bridge/status`

- `mode.kind`
- `mode.updated_at`
- `mode.reason`
- `mode.scope`

That alone would materially reduce frontend ambiguity.

## Why This Contract Matters

The frontend now presents state honestly enough that the remaining ambiguity is no longer visual. It is contractual.

Without these fields, the UI must keep inferring:

- whether the chain is quiet or stale
- whether the homepage is in snapshot mode because of relay posture or indexing posture
- whether low activity is real or a visibility gap

With these fields, the product becomes:

- more trustworthy
- easier to evaluate externally
- less likely to be misread as broken

## Summary

The next backend milestone is not broad API expansion. It is a targeted public freshness contract.

The public explorer needs explicit answers for:

- current chain head
- current indexed head
- latest visible transaction
- last non-empty block
- snapshot/feed mode
- subsystem freshness/completeness

That is the smallest backend addition with the highest frontend trust impact.
