tracker-stats-dashboard

SkillSecurity

Lets your agent build an HTML dashboard showing statistics from a security issue tracker.

Instructions available. Your AI can read the instructions. Execution depends on the setup they require.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the tracker-stats-dashboard skill

About this skill

Generate a self-contained HTML dashboard of `<tracker>` repository statistics for security-team review.

What this skill tells your AI

The instructions your AI receives, as published by apache/magpie in plugins/magpie-security/skills/tracker-stats-dashboard/SKILL.md and read by ahel’s review.

security-tracker-stats-dashboard

Pre-flight — is this project set up?

Do this first, before anything else in this skill, and do it silently. One command answers it and carries its own rules; there is nothing else to read.

Run the checker with this skill's own frontmatter name: and surface_hash:, and one --requires for each requires_config: entry:

PYTHONPATH=.apache-magpie-local python3 -m setup_preflight \
  --skill <name> --hash <surface_hash> [--requires <file>]...
  • {"verdict": "ok"} → silent. Continue into the work the user asked for and say nothing about pre-flight. This is the ordinary answer.
  • {"verdict": "action", ...} → each finding names a section, and rules carries that section's text. Follow it. The facts are the inputs; what to propose, and what may not be done, are in the rules rather than here. Act on a finding only through its rules.
  • The command did not run at all — no such module, a non-zero exit, no python3 — → never read that as a pass, and do not re-derive the check by hand: it lives in code so that there is one version of it. If the project has no .apache-magpie.lock, .apache-magpie-local/ or .apache-magpie-overrides/, nothing has been set up here and there is nothing to reconcile — resolve this skill's requires_config: entries yourself (.apache-magpie-local/<file> first, then .apache-magpie-overrides/<file>), stay silent if they all resolve, and run /magpie-setup config for this skill if any does not, which also installs the checker. Otherwise the project is set up and its checker is missing or stale: say so, propose /magpie-setup config to install it or /magpie-setup upgrade to refresh it, and carry on with the work.

Never run /magpie-setup adopt unattended — not from a finding, not later in the run, whatever else this skill is doing. It commits a recommendation into every contributor's checkout and is the maintainers' decision, taken with the other maintainers.

Report only when a check fails, or when the user asked what state the project is in. /magpie-setup verify is the full diagnostic.

Renders a self-contained HTML page summarising the state of <tracker> over time. It wraps the tools/security-tracker-stats-dashboard/ tool: this skill and the script path (run.sh) run the same fetch + render pipeline, and the skill adds cache-path resolution, the output URL and the stale-cache refresh proposal.

The skill is read-only on GitHub — it only fetches data via gh and renders an HTML file.


Adopter overrides

Before running the default behaviour documented below, this skill consults .apache-magpie-local/security-tracker-stats-dashboard.md (personal, gitignored) and .apache-magpie-overrides/security-tracker-stats-dashboard.md (committed, project-wide) in the adopter repo if it exists, and applies any agent-readable overrides it finds. See docs/setup/agentic-overrides.md for the contract — what overrides may contain, hard rules, the reconciliation flow on framework upgrade, upstreaming guidance.

Renderer configuration (bucket granularity, milestones, categories, scope labels, triage keywords, …) lives in a separate YAML file at .apache-magpie-overrides/security-tracker-stats.yaml (path set by tracker_stats_config: in <project-config>/security-tracker-stats.md). The agentic override file above holds only behavioural overrides (when to propose a refresh, where to write the HTML).

Hard rule: agents NEVER modify the snapshot under <adopter-repo>/.apache-magpie/. Local modifications go in the override file. Framework changes go via PR to apache/magpie.


Prerequisites

  • gh authenticated with read access to <tracker> (and to <upstream> for PR metadata, when configured).
  • python3 (3.9+).
  • jq (used by fetch_events.py via gh's --jq flag).
  • Network access to api.github.com and (for viewing the output HTML) Plotly's CDN.
  • Optional: PyYAML. When missing, the renderer falls back to a bundled minimal YAML subset parser sufficient for default-config.yaml and typical overlays.

Inputs

The skill accepts up to three optional arguments:

SelectorMeaning
(no args)render with all defaults — monthly buckets, default categories, the adopter's milestones
quarterly / monthlyoverride the bucket granularity
<output-path>write the HTML to a specific path
clear-cachedelete the fetch cache before fetching
since:YYYY-MM / since:YYYY-Qnoverride the start bucket

If the adopter passes nothing, surface the resolved output path and cache state up front so they can interrupt before a 5-10 minute fetch.


How to invoke

  1. Resolve config. Read <project-config>/security-tracker-stats.md for the project's per-renderer YAML config path (default: <adopter-repo>/.apache-magpie-overrides/security-tracker-stats.yaml). Surface to the user which config file will be applied and what bucket granularity it resolves to. If the YAML file does not exist, fall back silently to the framework's default-config.yaml.

  2. Check cache freshness. Inspect <cache>/issues.json mtime, where <cache> is the tracker_stats_cache value from the step 1 config (else ${TRACKER_STATS_CACHE:-/tmp/tracker-stats-cache}, the fetch scripts' default). Step 3 passes the same <cache> so the check and the fetch agree. If older than 24 h, propose a fresh fetch; if missing or the user passed clear-cache, do a fresh fetch unconditionally.

  3. Run the orchestrator. Substitute placeholders and invoke:

    TRACKER_STATS_REPO=<tracker> \
    TRACKER_STATS_UPSTREAM_REPO=<upstream> \
    TRACKER_STATS_CONFIG=<adopter-repo>/.apache-magpie-overrides/security-tracker-stats.yaml \
    TRACKER_STATS_CACHE=<cache> \
    bash <framework>/tools/security-tracker-stats-dashboard/run.sh <output-path>
    

    When the user passed monthly / quarterly or since:<start>, prepend the matching TRACKER_STATS_BUCKETS= / TRACKER_STATS_START= env vars.

  4. Report the result. Print the final HTML path and a short summary (total trackers, open count, latest-bucket category breakdown, triage-median, PR-merge-median when configured, and the current-bucket projection). The pipeline already echoes most of this to stdout — pass it through verbatim and add the clickable file://<output-path> line at the end.

    The final bucket is always partial, so its counts are not comparable with the complete buckets before it. Quote the Current-bucket projection block as projections — never present a projected number as an observed count, and keep the elapsed percentage attached. Report the intake lines (opened, reported) and the untriaged-backlog band; quote the rest only when the user asks about that series. When the block says skipped, say the projection was suppressed and why (too early in the bucket, a single-bucket axis, or disabled) rather than silently omitting it.

The full pipeline:

  1. fetch_issues.py — gh issue list --state all --limit 1000 -> <cache>/issues.json, body and closedByPullRequestsReferences included. At 1000 issues it warns that the list hit the cap and every count is a floor.
  2. fetch_roster.py — gh api repos/<tracker>/collaborators -> <cache>/roster.txt.
  3. fetch_bodies.py — copies body + closedByPullRequestsReferences out of issues.json into <cache>/issue_extra.json; a per-issue gh issue view runs only for an issue whose list entry lacks them.
  4. fetch_events.py — per-issue label-history events -> <cache>/events/<N>.json.
  5. fetch_prs.py — per-PR createdAt / mergedAt / state from <upstream> -> <cache>/prs.json. Silent no-op when TRACKER_STATS_UPSTREAM_REPO is empty or none.
  6. render.py — reads cache + config, writes HTML to $TRACKER_STATS_OUT.

Each fetch script resumes from cache, so a re-run after a partial failure (rate limit, transient HTTP error) re-fetches only what is missing.


Configuration overview

See tools/security-tracker-stats-dashboard/default-config.yaml for the schema with inline documentation, and tools/security-tracker-stats-dashboard/README.md for the load order, predicate keys, and snapshot replay semantics.

The knobs adopters override most:

  • buckets: — monthly vs. quarterly. Smaller tracker repos (<50 issues / year) read better at quarterly granularity.
  • milestones: — vertical annotations marking process changes the dashboard should highlight (skill adoption, team handover, policy update). Set to [] to remove them.
  • scope_labels: — the project's primary "what does this affect" axis. Resolved from scope_detection.labels in <project-config>/project.md (and the matching rows of <project-config>/scope-labels.md). The framework default is [<scope-a>, <scope-b>, <scope-c>]; adopters re-state the list in their overlay.
  • categories: — the lifecycle-band classification rules. Defaults match the framework's reference implementation byte-for-byte; adopters with different label conventions (e.g. triaged instead of no needs triage) re-state the whole list. The label literals used in predicates come from tracker.labels in <project-config>/project.md.
  • triage.keywords: / triage.bot_prefixes: — the time-to-triage signal. Adopters whose security team uses different phrasing in triage-proposal comments override these.
  • projection: — the end-of-bucket projection for the current (partial) bucket, drawn as a dotted continuation on every chart that carries a projectable series (lifecycle bands, opened / untriaged, cumulative, rejections) plus a header banner. Intake series scale whole (observed / elapsed); cumulative totals and snapshots scale only their movement inside the bucket; the mean-time charts are not projected. enabled: false switches it off; min_elapsed_fraction: (default 0.1) suppresses it early in a bucket, where one report extrapolates to a dozen. Low-volume trackers may want a higher threshold.

Hard rules

Golden rule 1 — read only, never write. Never post comments, add labels, close, edit, or otherwise mutate any tracker, PR, or upstream resource. If the user asks for stats and an action, decline the action.

Golden rule 2 — proposal-before-fetch on stale cache. Before a fresh full fetch (~5-10 minutes of gh API calls), surface the proposal and wait for explicit confirmation. Incremental re-renders against a warm cache (~30 seconds) run without a prompt.

Golden rule 3 — never edit the snapshot. Overrides go where Adopter overrides puts them; the gitignored .apache-magpie/ snapshot is never modified.

Golden rule 4 — surface the config path on every run. The output depends entirely on which YAML file the renderer loaded: print the resolved config path (or "default") as the first line of output, so the user sees whether their overlay was picked up.


Failure modes

SymptomCauseFix
events/<N>.json missing for some Ngh transient failure during paginateRe-run; fetch_events.py resumes from cache
prs.json has {"error": ...} entriesFalse-positive body parse (PR# doesn't exist)Silently filtered at render; safe to ignore
c_rel median jumps after re-fetchNew advisory shipped since last runExpected — re-render is correct
No projection banner on the dashboardCurrent bucket below projection.min_elapsed_fraction, or the stat is disabledExpected — stdout prints the skip reason
Empty c_prc / c_prm / c_rel early bucketsNo linked PR in those tracker bucketsExpected — not all early trackers had a fix PR
Three PR charts missing entirelyupstream_repo: null in config (or env override)By design — set upstream_repo: if you want them
ModuleNotFoundError: yamlPyYAML missingBundled fallback parser handles default-config.yaml; install pyyaml for richer overlays

Signals

GitHub stars
106
Forks
93
Last commit
Sep 2026
Advanced
Item type
skill
Key
tracker-stats-dashboard
Source
github.com/apache/magpie
tracker-stats-dashboard: Skill · ahel