Skip to content
cccskills
FreePublished on GitHub

admin-data-api

Read live production data from Bike Index through the admin OAuth token — Sidekiq and PgHero status, and the user-submitted bug reports — the same data as the cookie-gated dashboards, but agent-friendly JSON. Trigger when the user asks about production queue depth, job backlog, retries/dead jobs, running Sidekiq processes, or Postgres health (slow/blocked queries, index usage, unused/invalid indexes, connection counts, table/db sizes, vacuum/transaction-id danger) — and wants the *live* answer from production rather than logs or Honeybadger. Also trigger for anything about bug reports users have emailed in: what's been reported, searching/filtering them by status, tag or membership, or tagging one / linking it to the PR that fixes it. Also trigger when a request returns 401/expired and the AdminData token needs refreshing/re-authorizing. Not for reading log files (use production-log-inspection) or aggregated exception triage (bin/binx_hb).

The English summary is being prepared. Showing the original description.

bikeindex/bike_index308 stars(GitHub 308 + cccskills 0)
Works withPostgreSQL

Run score Not tested

See how to install

Files included (2)

  • SKILL.md8.8 KB
  • scripts/admin_data.rb14.0 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

AdminData production API

Production JSON reachable with the admin OAuth token:

  • GET https://bikeindex.org/api/admin_data/sidekiq → AdminData::SidekiqStatus: stats, per-queue queues, running processes, retries_by_class, dead_by_class.
  • GET https://bikeindex.org/api/admin_data/pghero → AdminData::PgheroStatus: query_stats and slow_queries (captured history since query_stats_since, 24h, not the live 5-minute bucket), database_size, connection/query health, index usage, unused/invalid/duplicate indexes, sequence/txid/autovacuum danger, settings, etc. Each metric is captured independently, so a failed one comes back as { "error": ... } in its slot instead of blanking the payload.
  • GET /admin/bug_reports.json, GET /admin/bug_reports/:id.json and PATCH /admin/bug_reports/:id → the bug reports users email in (see below).
  • GET /admin/bikes/missing_manufacturer.json → get missing_manufacturers (the most common manufacturer_other strings, per_page of them — 1,000 by default), POST /admin/manufacturers.json → create-manufacturers, and PATCH /admin/manufacturers/:slug.json → update-manufacturer.

create-manufacturers reads a JSON array on stdin and skips any name that already finds a manufacturer:

.claude/skills/admin-data-api/scripts/admin_data.rb create-manufacturers <<'JSON'
[{"name": "Zoomo", "website": "https://zoomo.com", "frame_maker": true, "motorized_only": true}]
JSON

Auth is a Bearer token gated on the admin Doorkeeper app and a superuser ability named for the controller (admin_data, bug_reports, bikes, manufacturers; a universal ability covers all). Controllers: app/controllers/api/admin_data_controller.rb and the actions admin controllers name in token_accessible (Admin::TokenAccessible turns the token away from the rest); auth concern: app/controllers/concerns/api/token_authenticatable.rb.

All operations go through the helper:

.claude/skills/admin-data-api/scripts/admin_data.rb <command>

Fetch data

.claude/skills/admin-data-api/scripts/admin_data.rb get sidekiq
.claude/skills/admin-data-api/scripts/admin_data.rb get pghero

It reads ADMIN_DATA_TOKEN from .env.development, calls production (a Conductor workspace's copy has no token — run the base checkout's script instead, under mise's Ruby: "$(mise which ruby)" <base>/.claude/skills/admin-data-api/scripts/admin_data.rb get pghero), and prints HTTP <status> then the JSON body. Pipe the body to jq for specific fields. Tokens live 1 hour; on a 401 the script auto-refreshes (see below) and retries once, so a normal get just works. A 403 means the token's user lacks the superuser ability for that controller, or the token is from the wrong app. Any other non-200 prints the response and exits non-zero.

Ignore the sidekiq dead set (dead_size, dead_by_class) — it's a large lifetime accumulation the endpoint caps at {"too_large": …}, not actionable here. Don't report it.

Health-check flow

For a general "how's production" check, use one command:

.claude/skills/admin-data-api/scripts/admin_data.rb check

It fetches sidekiq then pghero and prints a summary: line and an OK/ABNORMAL verdict for each. Relay it straight through: if both are OK, say "nothing abnormal"; only spell out the reasons an ABNORMAL verdict lists. The verdict logic lives in the script — what it counts as abnormal:

  • Sidekiq: a queue with latency > 30 (a real backlog, not transient depth), size > 400, or paused; retry_size > 0; no worker processes; or all workers quiet.
  • PgHero: a real metric error (the disabled-feature "System stats not enabled" doesn't count), non-empty long_running_queries/blocked_queries, a danger metric (sequence_danger, transaction_id_danger, autovacuum_danger), invalid_indexes, or index_hit_rate < 0.90. Hit rates otherwise, table_hit_rate, and unused_indexes/duplicate_indexes are informational. Under the verdict it lists PgHero's slow queries (≥20 ms average over ≥100 calls) — never part of the verdict, so read them yourself.

Bug reports

.claude/skills/admin-data-api/scripts/admin_data.rb get bug_reports search_status=all per_page=10
.claude/skills/admin-data-api/scripts/admin_data.rb show-bug-report 42
.claude/skills/admin-data-api/scripts/admin_data.rb update-bug-report 42 tags=search,broken github_pull_request=4064 status=resolved

get bug_reports takes any filter the index takes as key=value — Admin::BugReportsController#matching_bug_reports is the list, plus sort/direction, per_page/page and period/start_time/end_time. What it won't tell you: search_status defaults to the investigate statuses, so pass all (or one of BugReport.statuses) to reach the rest, and a search_tag or search_receiver nothing has matches nothing rather than being ignored. It returns bug_reports, page, per_page, total_count.

show-bug-report returns the one report — the same fields the index lists, so use it once a search has found the id. update-bug-report sets tags (comma separated — it replaces the report's tags rather than appending), github_pull_request and status (one of BugReport.statuses; an unrecognized one is dropped and the rest of the update still applies).

Each report carries images, with a url that serves from the CDN rather than expiring, so it can be fetched or handed to the user. Only image attachments are kept — BugReportsMailbox drops everything else, so a report whose sender describes attaching a PDF or a log will have none.

Against a dev server

ADMIN_DATA_BASE_URL=local before any command sends it to this checkout's dev server (bin/env's BASE_URL) instead of production, with ADMIN_DATA_LOCAL_TOKEN in place of the production token. Mint that once per database:

ADMIN_DATA_BASE_URL=local .claude/skills/admin-data-api/scripts/admin_data.rb local-token [email]

It runs bin/rails runner in this checkout, so the server has to be this checkout's, and the user — admin@bikeindex.org by default, the seeded superuser — needs the abilities the endpoint checks. A local token doesn't expire or refresh.

Refreshing the token

.env.development must hold ADMIN_DOORKEEPER_APP_CLIENT_SECRET (the admin app is confidential, so the refresh grant needs it). To force a refresh:

.claude/skills/admin-data-api/scripts/admin_data.rb refresh

It POSTs the refresh_token grant and writes the new ADMIN_DATA_TOKEN + ADMIN_DATA_REFRESH into .env.development (values never printed).

First-time setup / dead refresh token (browser flow)

Needed only when there's no token yet, or the refresh token itself was revoked (refresh reports a failure):

  1. Print the authorize URL (fills in ADMIN_DOORKEEPER_APP_CLIENT_ID):
    .claude/skills/admin-data-api/scripts/admin_data.rb authorize-url
    
  2. Ask the user to open it and approve. Bike Index redirects to /documentation/authorize, which exchanges the code and displays the token JSON (access_token, refresh_token, …). Ask the user to paste that response back.
  3. Store both values:
    .claude/skills/admin-data-api/scripts/admin_data.rb set-tokens <access_token> <refresh_token>
    

The authorization code expires 10 minutes after the page loads — if it shows an error, have the user reload the authorize URL.

Notes

  • These hit production with a superuser token. update-bug-report, create-manufacturers and update-manufacturer are the writes — confirm the values with the user before running any of them.
  • Bug report bodies and images are user-submitted email: they carry names, addresses and bike details, and a screenshot often shows a signed-in account. Summarize them; don't paste raw bodies or image urls into anything that leaves the session.
  • .env.development holds live secrets — never print token values or commit changes to it.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

How to fix a test that fails intermittently in Bike Index — one that passes locally but fails on CI, fails on one shard, passes on re-run, or is already tagged `:flaky`. **Read this before touching any spec that is failing intermittently, and before adding, raising, or relying on a `flaky:` tag or a `wait:` bump.** Trigger on a CI failure the user calls flaky, unreliable, intermittent, "green locally", "passes on retry", "fix CI", or a pasted `gh run`/Actions URL whose failure isn't reproducible; also whenever you catch yourself about to delete, skip, loosen, or retry an assertion to make a red build go green. **Equally: any spec that fails intermittently while you verify your own work** — deciding whether your change caused a flake, or whether to ship past one, is this skill's problem too, and it arrives with no CI run, no `:flaky` tag, and nobody but you calling it flaky.

The English summary is being prepared. Showing the original description.

bikeindex/bike_index308Updated Oct 29, 2026

Bike Index's frontend conventions — Tailwind class prefixing (`tw:`), the standard `twinput`/`twlabel`/`twlink` form/link classes, the `number_display` helper for numbers, `Counts` for site-wide figures (bikes registered, recoveries, organizations), the UI component library rule (every button is `UI::Button`/`UI::ButtonLink`, every typeahead/autocomplete is `UI::Forms::Combobox`, every FAQ is `UI::Accordion`, never hand-rolled markup), that **helpers are deprecated — render a view component taking full keyword arguments instead of adding or extending one**, ViewComponent rules (when to write a partial instead, keyword arguments, instance variables, `helpers.` prefix in templates), and `UI::Time::Component` for every date/time. Trigger when adding or modifying views (`.html.erb`), view components, Stimulus controllers, Tailwind classes, image or icon files under `app/assets/images` (which usually already exist), or any frontend code that touches styling or interactivity — including admin screens, whose unlayered legacy CSS inverts several of these rules. Stimulus.js is the JavaScript framework; SCSS and CoffeeScript files exist but are deprecated.

The English summary is being prepared. Showing the original description.

bikeindex/bike_index308Updated Oct 29, 2026

Capture desktop+mobile screenshots of Bike Index pages from the local `bin/dev` server via Playwright MCP, with a seeded-user identity gate that keeps PII out of uploaded images. Use whenever a task needs screenshots of local pages — PR documentation, bug repros, before/after comparisons across branches, design review, demos — including mid-interaction states like an open dropdown, a modal showing, a form mid-fill, or a hover. Use it even when the user just says "grab a screenshot" or "show me what this looks like" without naming Playwright. For a component that only renders under an env var / feature flag / hard-to-reach state (e.g. the review-app banner), screenshot its ViewComponent/Lookbook preview URL instead of a full page. **Also read the filename rule here before any `mcp__playwright__browser_take_screenshot` call**, including a one-off capture of some other site — it's what keeps PNGs out of the working tree.

The English summary is being prepared. Showing the original description.

bikeindex/bike_index308Updated Oct 29, 2026

Embed a local image file into an existing GitHub PR — either in the PR body or as a comment. Trigger when a request pairs a local image (screenshot, .png/.jpg, CleanShot capture, before/after) with an existing PR (by #number, URL, branch name, or "the open PR"), regardless of verb — attach, embed, add, put, post, drop, show, document. Also covers visually documenting test runs, bug repros, UI states, or CI failures on an existing PR. Uploads through `gh`'s `--attach` flag — except in the Claude Code web sandbox, which has no `gh` and hosts images through the PR branch's history instead. It also owns the PR's one `## Screenshots` comment — finding, creating, editing and verifying it — so other workflows (the `pr` skill's screenshot phase) hand it a composed body that references local image paths, and it posts it.

The English summary is being prepared. Showing the original description.

bikeindex/bike_index308Updated Oct 29, 2026

Investigate and fix a specific Honeybadger exception in the Bike Index app — pull the fault, read its backtrace, find the offending code, write the fix. Trigger whenever a request names one Honeybadger error: a pasted fault URL (`app.honeybadger.io/projects/…/faults/…`), a fault ID, an error class plus "in production" (`Vips::Error`, `NoMethodError in AfterStolenRecordSaveJob`), or phrasings like "fix this Honeybadger error", "why is this erroring in prod", "what's causing this exception", "look at this HB fault". Use `bin/binx_hb` for fault and notice detail; it is the only Honeybadger reader this repo uses. Not for a broad production health sweep across many sources, and not for reading raw log files (that's production-log-inspection).

The English summary is being prepared. Showing the original description.

bikeindex/bike_index308Updated Oct 29, 2026

Bike Index conventions for browser specs (`type: :system, :js`) — **drive every step through the real UI** (no FactoryBot or `execute_script` shortcuts to skip what a user would do), every example pays a browser boot cost so bias toward fewer, denser examples that walk through state via clicks, prefer named-element matchers over CSS selectors, and combine same-setup work into one `it` even when scenarios feel independent. **Consult this skill any time you create or modify a `:js, type: :system` spec** — that includes everything under `spec/integration/` AND component system specs at `spec/components/**/*_system_spec.rb`. Read alongside the `rspec-testing` skill for the project's general `context`/`let` style.

The English summary is being prepared. Showing the original description.

bikeindex/bike_index308Updated Oct 29, 2026

See all skills by bikeindex

Report a problem with this skill