Freshness diagnostics API, UI trust notes, mission control/stats updates, and deploy scripts.

Made-with: Cursor
This commit is contained in:
defiQUG
2026-04-12 06:33:54 -07:00
parent f46bd213ba
commit ee71f098ab
63 changed files with 5163 additions and 826 deletions

View File

@@ -0,0 +1,530 @@
# 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.

View File

@@ -0,0 +1,278 @@
# 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.

View File

@@ -1,5 +1,7 @@
# Track API Contracts
See also: [EXPLORER_FRESHNESS_DIAGNOSTICS_CONTRACT.md](/home/intlc/projects/proxmox/explorer-monorepo/docs/api/EXPLORER_FRESHNESS_DIAGNOSTICS_CONTRACT.md) for the public freshness and observability fields required by the current SolaceScan frontend.
Complete API contract definitions for all 4 tracks of SolaceScan Explorer.
## Track 1: Public Meta Explorer (No Auth Required)