# Explorer Freshness Implementation Checklist

This checklist converts the freshness contract into a backend implementation plan against the current SolaceScan code paths:

- [stats.go](/home/intlc/projects/proxmox/explorer-monorepo/backend/api/rest/stats.go)
- [mission_control.go](/home/intlc/projects/proxmox/explorer-monorepo/backend/api/rest/mission_control.go)

Use this document as the handoff from frontend trust requirements to backend delivery.

See also:

- [EXPLORER_FRESHNESS_DIAGNOSTICS_CONTRACT.md](/home/intlc/projects/proxmox/explorer-monorepo/docs/api/EXPLORER_FRESHNESS_DIAGNOSTICS_CONTRACT.md)
- [track-api-contracts.md](/home/intlc/projects/proxmox/explorer-monorepo/docs/api/track-api-contracts.md)

## Scope

This checklist covers four buckets:

1. field ownership and source of truth
2. response-shape rollout
3. freshness semantics
4. confidence and completeness behavior

## Bucket 1: Field Ownership And Source Of Truth

| Field | Endpoint | Backend owner | Source of truth | Directly measured or derived | Cadence | Nullable | Frontend dependency |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `freshness.chain_head.block_number` | `/api/v2/stats` | `stats.go` with RPC helper | authoritative public RPC head | directly measured | per stats request or short cache | no | homepage head freshness, blocks/trust cues |
| `freshness.chain_head.timestamp` | `/api/v2/stats` | `stats.go` with RPC helper | authoritative public RPC head block timestamp | directly measured | per stats request or short cache | no | head age, chain visibility |
| `freshness.latest_indexed_block.block_number` | `/api/v2/stats` | `stats.go` | explorer DB `MAX(blocks.number)` | directly measured | per stats request | no | distinguish head vs indexed head |
| `freshness.latest_indexed_block.timestamp` | `/api/v2/stats` | `stats.go` | explorer DB latest indexed block timestamp | directly measured | per stats request | yes until wired | detail-page trust cues |
| `freshness.latest_indexed_transaction.hash` | `/api/v2/stats` | `stats.go` | explorer DB latest indexed tx row | directly measured | per stats request | yes | activity summary |
| `freshness.latest_indexed_transaction.block_number` | `/api/v2/stats` | `stats.go` | explorer DB latest indexed tx row | directly measured | per stats request | yes | tx freshness explanation |
| `freshness.latest_indexed_transaction.timestamp` | `/api/v2/stats` | `stats.go` | explorer DB latest indexed tx row | directly measured | per stats request | yes | tx age, stale tx visibility |
| `freshness.latest_non_empty_block.block_number` | `/api/v2/stats` | `stats.go` | explorer DB latest block where `transaction_count > 0` or equivalent join | derived from indexed block/tx data | per stats request | yes | quiet-chain vs stale-visibility interpretation |
| `freshness.latest_non_empty_block.timestamp` | `/api/v2/stats` | `stats.go` | explorer DB latest non-empty block row | derived from indexed block/tx data | per stats request | yes | recent activity framing |
| `freshness.latest_non_empty_block.distance_from_head` | `/api/v2/stats` | `stats.go` | computed from chain head minus last non-empty block | derived | per stats request | yes | homepage block-gap explanation |
| `completeness.transactions_feed` | `/api/v2/stats` | `stats.go` | comparison of tx freshness vs head freshness | derived | per stats request | no | trust badges |
| `completeness.blocks_feed` | `/api/v2/stats` | `stats.go` | indexed block freshness vs chain head freshness | derived | per stats request | no | trust badges |
| `completeness.gas_metrics` | `/api/v2/stats` | `stats.go` | gas fields presence and quality | derived | per stats request | no | gas card honesty |
| `completeness.utilization_metrics` | `/api/v2/stats` | `stats.go` | utilization field presence and quality | derived | per stats request | no | utilization card honesty |
| `sampling.stats_generated_at` | `/api/v2/stats` | `stats.go` | server clock at response generation | directly measured | per response | no | “updated” copy |
| `sampling.rpc_probe_at` | `/api/v2/stats` | `stats.go` | latest successful RPC sample timestamp | directly measured or nullable | per stats request | yes | source confidence |
| `mode.kind` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | mission-control feed mode | directly measured if known, otherwise derived conservatively | per response / SSE tick | no | snapshot/live messaging |
| `mode.updated_at` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | mission-control snapshot timestamp | directly measured | per response | no | snapshot age |
| `mode.reason` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | bridge/homepage mode controller | directly measured if available, else nullable | per response | yes | scope explanation |
| `mode.scope` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | bridge/homepage mode controller | directly measured if available, else nullable | per response | yes | “what is affected?” |
| `subsystems.rpc_head.*` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | RPC probe result | directly measured | per response | no | mission-control trust cues |
| `subsystems.tx_index.*` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` using stats freshness or shared helper | explorer DB tx freshness | derived from authoritative indexed data | per response / shared cache | yes | homepage stale-tx explanation |
| `subsystems.bridge_relay_monitoring.*` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | existing relay probe payload | directly measured | per response | no | lane posture |
| `subsystems.stats_summary.*` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` or shared summary helper | stats freshness sample | derived | per response | yes | homepage summary confidence |

## Bucket 2: Response-Shape Rollout

### Ship immediately as nullable additions

These are low-risk additive fields that can be introduced without breaking existing clients.

- `freshness.latest_indexed_transaction.*`
- `freshness.latest_non_empty_block.*`
- `freshness.latest_indexed_block.timestamp`
- `sampling.stats_generated_at`
- `sampling.rpc_probe_at`
- `mode.kind`
- `mode.updated_at`
- `mode.reason`
- `mode.scope`
- `subsystems.*`

### Ship after backend wiring

These need real data acquisition or shared helpers.

- `freshness.chain_head.*`
- `completeness.transactions_feed`
- `completeness.blocks_feed`
- `completeness.gas_metrics`
- `completeness.utilization_metrics`

### Derived computations

These may be computed in backend code once the authoritative inputs exist.

- `freshness.latest_non_empty_block.distance_from_head`
- subsystem `status`
- completeness enums
- optional `activity_interpretation`

### Frontend adoption order

1. Prefer new fields when present.
2. Fall back to current inference when absent.
3. Remove inference once fields are stable in production.

## Bucket 3: Freshness Semantics

Each field must answer a precise question.

### `freshness.chain_head`

- Meaning: latest chain head observed from the authoritative public RPC
- Must not mean: latest indexed explorer block
- If unknown: return `null` object members where needed plus completeness/confidence state

### `freshness.latest_indexed_block`

- Meaning: latest block successfully indexed into the explorer DB or visible explorer block source
- Must not mean: latest RPC head

### `freshness.latest_indexed_transaction`

- Meaning: latest transaction currently visible in the public indexed transaction feed
- Must not mean: latest mempool event or latest raw RPC tx if not visible in the explorer feed

### `freshness.latest_non_empty_block`

- Meaning: latest indexed block containing at least one visible indexed transaction
- This is the critical disambiguator for quiet-chain vs stale-visibility interpretation

### `mode.kind`

- Meaning: the current homepage/mission-control delivery mode
- Allowed values: `live`, `snapshot`, `mixed`, `unknown`

### `mode.scope`

- Meaning: which user-visible surface is affected by mode choice
- Examples:
  - `relay_monitoring_homepage_card_only`
  - `homepage_summary_only`
  - `bridge_monitoring_and_homepage`

### `mode.reason`

- Meaning: why snapshot or mixed mode is active
- Must be calm and operational, not blame-oriented
- Examples:
  - `live_homepage_stream_not_attached`
  - `relay_snapshot_only_source`
  - `partial_observability_inputs`

### `subsystems.*`

- Meaning: freshness of each component, not overall product health
- Recommended subsystem keys:
  - `rpc_head`
  - `tx_index`
  - `bridge_relay_monitoring`
  - `stats_summary`

## Bucket 4: Confidence And Completeness

Every nullable or derived field should have explicit semantics.

### Confidence

- `high`: authoritative source and recent sample
- `medium`: authoritative source but partially stale, or a stable derived value from strong inputs
- `low`: weakly derived or missing one of the underlying inputs
- `unknown`: no basis to express confidence

### Completeness

- `complete`: field is current and supported by recent source data
- `partial`: field exists but some required inputs are missing or weak
- `stale`: field is known, but the latest available value is older than acceptable freshness
- `unavailable`: no trustworthy value exists

### Null and zero handling

- Unknown must be `null`, not synthetic `0`
- Zero may be returned only when zero is a real measured value
- If a value is null, a sibling completeness/confidence field must explain why

## Acceptance Tests

These should be implemented in backend tests and used as rollout gates.

### 1. Current head, stale tx visibility

If chain head is current but tx visibility is stale:

- `freshness.chain_head` must be current
- `freshness.latest_indexed_transaction` must be older
- `freshness.latest_non_empty_block` must be exposed
- completeness must not report all feeds as `complete`

### 2. Quiet chain, current visibility

If recent head blocks are genuinely empty:

- `freshness.chain_head` must still be current
- `freshness.latest_non_empty_block` must be present
- `freshness.latest_indexed_transaction` must be present
- API must not force a stale diagnosis if visibility itself is current

### 3. Snapshot mode active

If snapshot mode is active:

- `mode.kind` must be `snapshot` or `mixed`
- `mode.scope` must state what is affected
- `mode.reason` must be present if known

### 4. Unknown fields

If a field is unknown:

- return `null`
- expose confidence/completeness state
- do not return fake zero values

## Backend Implementation Checklist

### `stats.go`

- [ ] Extend `explorerStats` with nullable freshness/completeness/sampling fields.
- [ ] Add query/helper for latest indexed transaction.
- [ ] Add query/helper for latest non-empty block.
- [ ] Add query/helper for latest indexed block timestamp.
- [ ] Add RPC helper for current chain head number and timestamp.
- [ ] Compute `distance_from_head` when both chain head and latest non-empty block are present.
- [ ] Compute completeness enums for blocks, transactions, gas metrics, and utilization.
- [ ] Return `null` for unknowns rather than synthetic zero values.
- [ ] Add internal tests covering:
  - healthy current state
  - quiet-chain state
  - stale tx visibility state
  - null/unknown field handling

### `mission_control.go`

- [ ] Extend bridge status response with `mode`.
- [ ] Extend bridge status response with `subsystems`.
- [ ] Reuse or call shared freshness helper for tx index freshness rather than duplicating logic.
- [ ] Emit `mode.scope` and `mode.reason` only when backend can support them.
- [ ] Use `unknown` or nullable values when reason/scope cannot be stated authoritatively.
- [ ] Add tests covering:
  - live mode
  - snapshot mode
  - mixed mode
  - tx index stale while RPC head remains current

### Shared rollout

- [ ] Frontend reads new fields opportunistically.
- [ ] Existing frontend inference remains as fallback until backend fields are stable.
- [ ] Swagger/OpenAPI docs updated after implementation.
- [ ] Public docs updated only after payload shape is live.

## Test Coverage Guidance

For every field, capture:

- who computes it
- from what source
- at what cadence
- whether nullable or required
- fallback behavior
- confidence/completeness semantics
- frontend dependency
- backend test case name

That metadata is more important than perfect initial coverage breadth.

## Shortest Path To Value

If the team wants the fastest possible trust win, implement these first:

1. `freshness.chain_head`
2. `freshness.latest_indexed_transaction`
3. `freshness.latest_non_empty_block`
4. `sampling.stats_generated_at`
5. `mode.kind`
6. `mode.scope`
7. `mode.reason`

That is the minimum set that lets the frontend stop guessing about the most visible freshness ambiguity.
