Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Viewer Parity

ASecurity

Use when adding or changing what the rezolus viewer exposes — an /api/v1 endpoint, a dashboard section, the metric catalog, metadata/description derivation, or the frontend data those depend on — or when editing src/viewer/ or crates/viewer/, or reviewing a viewer change. The viewer ships as two separate Rust backends (server + WASM) behind one shared frontend; changing one silently diverges them.

275 stars
0 votes
0 copies
0 views
Added 9/12/2026
devopsrustgoshellgitapifrontendbackend

Works with

api

Security Analysis

A100/100

Scanned 9/12/2026

$npx -y skills add iopsystems/rezolus --skill viewer-parity --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Viewer Parity?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Viewer Parity
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/iopsystems-viewer-parity/badge)](https://www.skillsdirectory.com/skills/iopsystems-viewer-parity)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: viewer-parity
description: Use when adding or changing what the rezolus viewer exposes — an /api/v1 endpoint, a dashboard section, the metric catalog, metadata/description derivation, or the frontend data those depend on — or when editing src/viewer/ or crates/viewer/, or reviewing a viewer change. The viewer ships as two separate Rust backends (server + WASM) behind one shared frontend; changing one silently diverges them.
---

# Viewer parity (server ↔ WASM)

## Overview

`rezolus view` ships as **two backends behind one shared frontend**:

- **Server** — the `rezolus` binary, `src/viewer/*.rs` (axum HTTP: `routes.rs`,
  `metadata.rs`, `state.rs`, `actions.rs`, `source_kind.rs`, …).
- **WASM** — the separate `crates/viewer` crate, `crates/viewer/src/lib.rs`,
  which loads a parquet in-browser for the static GitHub-Pages site.
- **Shared frontend** — most of `src/viewer/assets/lib/**` is **symlinked**
  file-for-file into `site/viewer/lib/`, so both backends serve the **same**
  JS/CSS. **Two files are NOT symlinks** — `viewer_api.js` and `script.js` are
  **separately maintained copies** in `site/viewer/lib/`. `viewer_api.js` is the
  backend-adapter seam (server does HTTP `fetch`; WASM calls into the in-browser
  registry), so it *must* differ — which means a change to the server's
  `viewer_api.js` does **not** propagate to the WASM shell. Editing either →
  mirror the other copy.
  - **A new shared module under `src/viewer/assets/lib/` needs a resolving
    entry under `site/viewer/lib/`** (a per-file symlink, or coverage by a
    parent directory symlink like `site/viewer/lib/embed`) — or the deployed
    static viewer 404s on the import and **fails to load entirely**. This is
    now enforced in CI (`viewer-symlinks.yml` → `scripts/check-viewer-symlinks.sh`);
    run it locally before pushing a viewer change. (A missing `charts/boxplot.js`
    symlink shipped exactly this outage before the guard existed.)

`crates/viewer` **cannot depend on the `rezolus` binary crate** — `rezolus` is
binary-only, with no `lib` target — so any backend logic both need must live in a
shared crate (**`dashboard`**, **`rez`**) or be **duplicated** in `lib.rs`. That
structural gap is why viewer behavior gets added to one side and silently missing
from the other. It is also why the `.rez` reader was unreachable from the browser
for as long as it lived in `src/`: moving it to `crates/rez` is what let the
static-site viewer open an archive at all.

Two things break in the browser without failing to compile, so watch for them in
anything the WASM side will reach:

- **`metriken` (and `metriken-exposition`)** — their registry declares a `linkme`
  distributed slice and `linkme` has no wasm32 implementation. This one IS a
  compile error, but only in a wasm32 build: `cargo check -p rez
  --no-default-features --target wasm32-unknown-unknown` is the CI guard.
- **Threads** — `std::thread::spawn` compiles for wasm32 and panics at runtime.

**Core principle: a change to what the viewer *derives from a loaded recording*
is a change to BOTH backends.** Shipping it on one is a parity regression, not a
smaller scope. (Real instance: per-source classification was added to the server's
`metadata.rs` and never mirrored in `lib.rs`, so simple-capture parquets showed no
`source:` section in the WASM viewer — see
[per-source descriptions](../../../docs/journal/2026-07-04-per-source-descriptions.md)
and the simple-capture entry.)

## The one legitimate exception

Something is **server-only** *only* when the capability structurally can't exist
in the browser: talking to a **live agent** (`/connect`), **proxy-fetching** a URL
(`/load_url`), server **reset**, upload plumbing. WASM operates on one
already-loaded parquet. Everything that is a **pure derivation from the loaded
recording** — sections, metric catalog, metadata, systeminfo, descriptions,
PromQL — exists on both. "It's just the server endpoint" is not an exception; it's
the regression.

## Server route ↔ WASM method map

The shared `viewer_api.js` calls `/api/v1/<x>` via `backendRequest`; the server
answers with an axum handler, WASM answers with a `lib.rs` method routed through a
shim. A new frontend-facing endpoint needs an entry on **both** sides or WASM 404s.

| Frontend calls | Server (`src/viewer/routes.rs`) | WASM (`crates/viewer/src/lib.rs`) |
|---|---|---|
| `/api/v1/sections`, `/data/<s>.json` | `sections_handler`, `data` | `get_sections`, `get_section` |
| `/api/v1/metrics` | `metrics_handler` | `metrics` |
| `/api/v1/systeminfo` | `systeminfo_handler` | `systeminfo` |
| `/api/v1/file_metadata` | `file_metadata_handler` | `file_metadata_json` |
| `/api/v1/metadata` | `metadata` | `metadata` |
| `/api/v1/selection` | `selection_handler` | `selection` |
| `/api/v1/query`, `/query_range` | `instant_query`, `range_query` | `query`, `query_range` |
| `/api/v1/save*` | `actions::save*` | `save_with_selection` |
| dashboard build (not an endpoint) | `metadata::regenerate_dashboards` → `classify_sources` | `init_templates` / `regenerate_combined` |
| `/api/v1/connect`, `/load_url`, `/reset` | server handlers | **none — server-only by nature** |

## How to keep them in parity

1. **Put the logic in `dashboard`, call it from both shells.** The endpoint
   handler and the WASM method are different *shells* (axum extractors vs
   `#[wasm_bindgen]`), but the *logic* should be one function in the `dashboard`
   crate that both call — like `metric_catalog::assemble_catalog` and
   `metric_catalog::resolve_descriptions`. Byte-identical output falls out for
   free. Do NOT copy logic into `lib.rs` when it can be shared.
2. **When a shell must differ, change both shells in the same PR.** Adding a route
   handler? Add the matching WASM method. Adding classification/section logic in
   `metadata.rs`? Mirror it in `init_templates`.
3. **Most frontend changes hit both automatically** (symlinked assets) — but only
   if the endpoint they call exists on both backends. Verify the WASM side answers
   it. Exception: `viewer_api.js` and `script.js` are per-shell copies, not
   symlinks — a change to one must be mirrored into the other.
4. **Duplicated files are a hazard.** Known hand-mirrored pairs: `report_save.rs`
   (`src/viewer/` **and** `crates/viewer/src/`), and the frontend `viewer_api.js` /
   `script.js` (`src/viewer/assets/lib/` **and** `site/viewer/lib/`). An edit to
   one copy must be mirrored to the other. Prefer folding backend duplication into
   `dashboard`; the `viewer_api.js` split is intrinsic (the adapter seam differs).

## Tests — so one side can't be silently ignored

Compilation parity is necessary but **not sufficient** — a WASM build can be green
while the WASM viewer produces different (or empty) output.

- **Build gate (necessary):** `cargo check -p viewer --target wasm32-unknown-unknown`
  and `./crates/viewer/build.sh`. Catches "forgot to compile the WASM side",
  never "forgot to implement the behavior".
- **Behavioral parity (the real gate):** for a set of fixture parquets (at least
  one Rezolus recording, one service/combined file, and one non-Rezolus *simple
  capture* like `hub.heartbeat.parquet`), assert the **server endpoint JSON and the
  WASM method JSON are byte-identical** for the same input — `sections`, `metrics`,
  `systeminfo`, `file_metadata`, `metadata`. A Rust test can construct
  `crates/viewer`'s `Viewer` from the fixture bytes and diff its `metrics()` /
  `get_sections()` output against the server path's output for the same fixture.
  When you add a derivation, add its fixture to this set — a simple-capture fixture
  is what would have caught the missing WASM `source:` section.
- **Smoke coverage is server-only today.** `tests/viewer_smoke.sh` exercises only
  the axum server (see the `viewer-smoke` skill). Adding a curl+jq check there does
  **not** cover WASM. Parity needs the WASM-side check above.

## Which side am I forgetting? (pre-PR checklist)

- Did I change what the viewer derives from a recording (sections/catalog/metadata/
  descriptions/query)? → it belongs on **both** backends.
- Is my new logic in `dashboard` (shared), or did I write it in `src/viewer/` only?
  If server-only, does WASM need it? (Almost always yes for derivations.)
- New `/api/v1/*` route the frontend calls? → matching `lib.rs` method added.
- Did I add a fixture-diff parity assertion, or only a server-side smoke check?
- Edited `report_save.rs`, `viewer_api.js`, or `script.js`? → mirror the other copy
  (these are hand-maintained duplicates, not symlinks).

## Rationalizations — STOP

| Excuse | Reality |
|---|---|
| "The task said the server endpoint, so WASM is out of scope." | A derivation feature on one backend is a parity regression. Scope is *the behavior*, which lives on both. |
| "I'll do the WASM side as a separate follow-up." | Follow-ups for parity don't happen; the WASM viewer ships broken meanwhile. Same PR. |
| "`cargo check -p viewer` passes, so WASM is fine." | Compilation ≠ behavior. An empty/wrong WASM result compiles clean. |
| "The frontend is shared, so it just works in both." | Only if the endpoint it calls exists on both. Shared frontend hitting a WASM-missing endpoint = silent 404. |
| "It's a small addition." | Small divergences are the ones that ship — no reviewer notices one missing method. |

Attribution

iopsystemsiopsystems
View sourceSee grades on GitHubMore from iopsystems →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Terraform Module Library

Build reusable Terraform modules for AWS, Azure, and GCP infrastructure following infrastructure-as-code best practices. Use when creating infrastructure modules, standardizing cloud provisioning, or implementing reusable IaC components.

401991 votes

sematext-otel

Wire a service's OpenTelemetry output to Sematext Cloud. Walks through region, App-type, instrumentation flow (managed OTLP endpoint vs Sematext Agent), and signal selection (traces/metrics/logs), then produces the exact env-var block and points at a runnable reference example in this repo. Invoke when instrumenting a new app for Sematext.

01 votes

Deployment Patterns

Deployment workflows, CI/CD pipeline patterns, Docker containerization, health checks, rollback strategies, and production readiness checklists for web applications. Use when setting up deployment infrastructure or planning releases.

2699140 votes

Babysit

Watch a pull request or review cycle until it is ready to merge. Use when asked to babysit, monitor, or keep checking PR comments, reviews, and CI until all actionable issues are resolved.

968770 votes

V7 Roster

Interact with the Paperclip control plane API for task coordination and governance. Use when checking assignments, updating issue status, posting comments, delegating work, managing routines, or calling Paperclip API endpoints.

953190 votes
View all in devops →