Squid perf
SQD's official collection of AI agents skills
npx -y skills add subsquid-labs/skills --skill squid-perfAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Compare sync-time performance across one or more Squid SDK deployments. Fetches logs via sqd CLI, parses per-service progress, and generates a self-contained HTML report plus a Markdown summary with wall-clock/active-time/downtime breakdowns at log-spaced block breakpoints. Supports single-indexer mode (metrics only, no comparison). Use when the user invokes "/squid-perf", asks to compare Squid deployment sync times, or references squid performance profiling.
SKILL.md
12.2 KB, as published. Nobody here has run it
/squid-perf
Compare sync-time performance across one or more Squid SDK deployments. Produces a self-contained HTML report and a Markdown summary in the current working directory.
Skill dir: ~/.claude/skills/squid-perf/ (scripts live in scripts/)
Output dir: ./squid-perf-output/ (CWD-relative; created if missing)
Locked-in design (do not re-litigate)
These are settled — don't ask the user again unless they change something.
- Comparison unit: per-service (e.g., compare
settlement-arbitrumin deployment A vs B independently of other services). Services present in every compared deployment go in comparison tables; services present in only some go in a "solo metrics" section with a warning. - Sync time metric: report wall-clock elapsed (headline), active processing time (excludes gaps > 120s — configurable via
--downtime-threshold), and downtime (wall − active). All three per breakpoint. - Block alignment: assume all compared indexers cover the same block ranges (user's stated assumption). Use relative-from-first-log per deployment. If detected ranges diverge noticeably across deployments for a given service, emit a loud warning in the summary but still render.
- Tier of metrics extracted:
- Tier 1 (always):
sqd:processor/sqd:batch-processorprogress lines →(ts, current_block, target_block, rate, mapping_rate, items_per_sec, eta). - Tier 2 (always if present):
sqd:multicalllatency lines, restarts (detected viacurrent_blockgoing backward), ERROR/WARN lines (capped at 1000/service). - Tier 3 (auto-discovered): any logger namespace appearing ≥10 times in ALL compared deployments for the SAME service; extract numeric fields; render as a small stats table (count, mean, median, p95).
- Tier 1 (always):
- Breakpoint selection: percentage-based — 10 evenly-spaced breakpoints at 10%, 20%, ..., 100% of each service's effective range, where effective range =
(catchupBlock - firstBlock).catchupBlock= first progress row wherecurrent >= target - 10(indexer reached chain tip). Anything past this is steady-state, not sync, and is excluded from the metric.- Rationale: fraction-based clips (e.g., "99.9% of observed range") fail when the idle tail has many progress rows but few blocks — a deployment synced in 4 min and idled 10 days ends up with 99.9% of its blocks still inside the sync phase but 100% of the time inside the tail.
- If a service has no catchup point in the logs (
stillSyncing), falls back to actuallastBlockwith a "never caught up" warning. - If a service was already caught up at the first progress row (
wasAlreadyCaughtUp), the entire captured window is the idle tail — emit a warning and fall back tolastBlock(metrics reflect steady-state latency, not sync). - For multi-deployment comparison: shared effective range =
min(catchupBlock across deployments) - firstBlock. - Output surfaces both the percentage and the absolute block count (e.g., "10% (500K blocks)"). Override via
--breakpoints 500K,1M,5M,10M,20M(absolute block offsets from firstBlock; catchup logic does NOT apply to overrides).
- Catchup gap threshold:
CATCHUP_GAP_BLOCKS = 10(constant inreport.mjs). Matches the indexer's own steady-state lag behind chain head; tuneable if a chain's head noise is higher. - Output: self-contained HTML (Chart.js inlined — no CDN), plus Markdown with tables only (no charts in MD, link to HTML at the top).
- HTML template: the HTML report MUST be rendered from the template at
~/.claude/skills/squid-perf/templates/report.html.report.mjsreads this template and substitutes placeholders rather than building markup via string concatenation. Edit the template to change layout/styling; do not inline HTML in the script. - Output layout:
./squid-perf-output/ ├── cache/ │ └── <ref-slug>__<since>.log (+ .done sentinel) ├── <ISO-timestamp>/ │ ├── compare-syncs.json │ ├── parsed/<ref-slug>.json │ ├── report.html │ ├── report.md │ └── run.log └── latest -> <ISO-timestamp>/ - Caching:
(ref, since)keyed;.donesentinel written atomically after successful fetch.--force-refreshignores cache. Interrupted runs (no sentinel) are treated as absent. - Fetching: parallel Bash (not subagents), one per deployment, wraps
sqd logsinexpectto handle the"type \"it\" to fetch more logs"pagination prompt. Retries 3× with 10s backoff. Partial success is OK (continue with ≥1 fetched deployment); if all fail, abort. - Script language: Node (ESM, zero deps). Uses stdlib only. Shell scripts use bash + expect.
- Still-syncing detection: if last log timestamp ≤ 60s of fetch-start, flag as "live/partial" in summary.
Orchestration
Execute the following phases in order. Mark progress with TaskCreate/TaskUpdate as you go.
Phase 0 — Preflight
Run bash ~/.claude/skills/squid-perf/scripts/preflight.sh.
- On non-zero exit: print the script's error output verbatim, then stop. Do not proceed.
- On zero exit: continue.
Phase 1 — Collect inputs
Parse the slash-command args. Accepted shapes:
/squid-perf <ref1> [<ref2> ...]
/squid-perf --config path/to/compare-syncs.json
/squid-perf <ref1> <ref2> --breakpoints 500K,1M,5M --downtime-threshold 180 --force-refresh
Flags (all optional):
--config <path>: read refs/since/labels from JSON (skips prompts entirely).--breakpoints <csv>: override auto-generated breakpoints. AcceptsK/M/Bsuffixes.--downtime-threshold <seconds>: gap > N seconds counts as downtime (default 120).--force-refresh: ignore cache, re-fetch every deployment.
For each positional ref the user supplied:
- Regex-validate the shape:
^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+$. On invalid shape, report which ref is malformed and stop. - If
sinceorlabelfor that ref is missing (not in config), ask viaAskUserQuestionin a single batched call:- "Deployment date for
<ref>(ISO 8601 withZ, e.g.,2026-04-16T08:30:59Z) — used forsqd logs --since?" - "Short label for
<ref>(e.g., 'baseline', 'optimized')? Default: derived from ref name."
- "Deployment date for
- After collecting: resolve label collisions (append
_2,_3, …).
If zero positional refs and no --config, prompt: "Which indexer refs would you like to compare? Enter space-separated refs in the form <org>/<name>@<hash>, or just one for single-indexer mode."
Write the resolved config to <run-dir>/compare-syncs.json:
{
"createdAt": "2026-04-21T14:22:03Z",
"downtimeThresholdSec": 120,
"breakpointsOverride": null,
"indexers": [
{ "ref": "void/gmx-optimized-multichain-v2@oe4zvr", "since": "2026-04-16T08:30:59Z", "label": "optimized" },
{ "ref": "void/gmx-baseline-multichain-v1@xyz123", "since": "2026-03-20T12:00:00Z", "label": "baseline" }
]
}
Where <run-dir> is ./squid-perf-output/<now-ISO-Z-with-colons-replaced-by-dash>/. Create it now; also ./squid-perf-output/cache/ if missing.
Phase 2 — Fetch logs (parallel Bash)
For each indexer in the resolved config, compute:
slug = ref.replace(/[\/@:]/g, "-")(filesystem-safe)cache_path = ./squid-perf-output/cache/<slug>__<since-with-colons-as-dashes>.logsentinel_path = <cache_path>.done
If sentinel_path exists and --force-refresh is NOT set: skip fetch, reuse cache.
Otherwise, launch the fetch in parallel — one Bash(run_in_background=true) call per indexer:
bash ~/.claude/skills/squid-perf/scripts/fetch-logs.sh <ref> <since> <cache_path>
After launching all background Bash calls, poll them with BashOutput until each finishes. Don't sleep proactively — the runtime notifies on completion.
The fetch script handles:
- Retry logic (3× with 10s backoff).
- Writes to
<cache_path>.partialthen atomic rename +.donesentinel on success. - On permanent failure: exits non-zero with a clear error on stderr.
After all fetches return:
- For each failure, record in
failed_fetches[]. - If all failed: write a minimal
report.mdwith the failure summary, print path, exit. - Otherwise continue with whichever deployments succeeded. If only 1 left: single-indexer mode.
Phase 3 — Parse
For each successfully-fetched deployment, run (can be parallel, but sequential is fine — parse is fast):
node ~/.claude/skills/squid-perf/scripts/parse.mjs \
--input <cache_path> \
--output <run-dir>/parsed/<slug>.json \
--label <label>
The parser streams the log, emits structured JSON per service. See scripts/parse.mjs for the exact schema it produces.
If parse fails for a deployment: record, continue. If all fail: stop with error.
Phase 4 — Compute metrics & render
node ~/.claude/skills/squid-perf/scripts/report.mjs \
--run-dir <run-dir> \
[--breakpoints <csv>] \
[--downtime-threshold <seconds>]
This reads <run-dir>/compare-syncs.json + all <run-dir>/parsed/*.json, computes breakpoints per service, calculates wall/active/downtime at each breakpoint per deployment, runs Tier-3 auto-discovery, generates the summary findings, writes:
<run-dir>/report.html(self-contained)<run-dir>/report.md
Phase 5 — Finalize
- Update the
./squid-perf-output/latestsymlink atomically:ln -sfn <ISO-timestamp-dir> ./squid-perf-output/latest - Print to the user:
✓ Report: ./squid-perf-output/<id>/report.html Markdown: ./squid-perf-output/<id>/report.md Cached logs: ./squid-perf-output/cache/ (re-used on next run) Failed fetches: <list or "none"> - Mark all tasks completed.
Error handling
- Preflight fails: stop immediately, print install instructions from the script. Never proceed without sqd/expect/node/auth.
- Malformed ref: stop before fetching, surface the exact bad ref.
- Fetch fails for one deployment: continue with the rest. Summary gets a prominent warning.
- Fetch fails for all: write a minimal failure report and stop.
- Parse fails: treat like fetch failure for that deployment.
- Interrupted (Ctrl-C): partial cache files have no
.donesentinel, so next run re-fetches. Never treat a partial as complete. - Live deployment: if a deployment's most recent log is within 60s of fetch start, flag
"live": truein parsed JSON; renderer prints a warning. - Range divergence: if two deployments' first-block or max-block differ by > 5% for the same service, emit a warning banner in summary.
- Service missing from some deployments: intersection only for comparison tables; solo section per missing service; warning in summary.
Single-indexer mode
Triggered when only 1 ref is supplied (or only 1 fetch succeeded). Behavior:
- Skip "verdict" findings (no comparison).
- HTML/MD still show breakpoint table + Tier 2/3 stats per service.
- Line chart shows one series (that deployment).
- Summary block just lists services, block ranges, wall-time-to-end, and warnings.
Notes for future maintenance
- The
"type \"it\" to fetch more logs"pagination prompt is hard-coded infetch-logs.sh. If the sqd CLI changes its prompt, update that string. - Chart assets ship inside
templates/report.htmlas part of the bundler manifest;report.mjsdoesn't fetch or embed Chart.js itself. To update the client renderer, re-bundle and replace the template file. report.mjsinjects aReportDataJSON payload (schema defined insidetemplates/report.htmlas a TEMPLATE CONTRACT comment) into the<script id="__REPORT_DATA__">slot. If the schema changes, update both the template contract andbuildReportDatain lockstep.- To extend Tier-3 discovery (e.g., add known log shapes), edit
parse.mjs'sTIER3_EXTRACTORS.