[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6J4KSYCBC348GY9MCSRR5AX":3,"skill-same:01M6J4KSYCBC348GY9MCSRR5AX":82},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":30,"publishedAt":48,"linkOnly":49,"markdownContent":50,"markdownBody":51,"frontMatter":52,"refName":58,"commitSha":59,"skillDirectory":60,"forksCount":61,"repositoryHtmlUrl":62,"repositoryHomepageUrl":63,"ownerHtmlUrl":64,"ownerBlogUrl":65,"ownerDescription":66,"updatedAt":67,"install":68,"resources":81},"01M6J4KSYCBC348GY9MCSRR5AX","skill-health",null,"Fleet skill observability with two views - health audits per-skill metrics and files\u002Fresolves issues in memory\u002Fissues\u002F; analytics ranks the fleet by 7d runs, success rates, and anomaly flags.","MiroShark\u002Fmiroshark-aeon","MiroShark","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F304594232?v=4","01M6J466B8NNAZ4H6NNK3N3TT5","01M6J466FXNNWH622S3B9PC496",20,"MIT","https:\u002F\u002Fgithub.com\u002FMiroShark\u002Fmiroshark-aeon\u002Fblob\u002Fmain\u002Fskills\u002Fskill-health\u002FSKILL.md",[17,22,25],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000013","分析与可视化","SCENARIO","使用场景",{"categoryNo":23,"categoryName":24,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":26,"categoryName":27,"taxonomyType":28,"taxonomyTypeDesc":29},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47],"skill health","health","anomaly","memory","fleet","with","false","flags","files","evolution","in memory","Meta","observability","metrics","issues","skill metrics","analytics","2026-11-10 10:12:30",false,"---\nscorable: false  # meta skill: no gradable output, skip the post-run quality scorer\nname: skill-health\ndescription: Fleet skill observability with two views - health audits per-skill metrics and files\u002Fresolves issues in memory\u002Fissues\u002F; analytics ranks the fleet by 7d runs, success rates, and anomaly flags.\nscorable: false  # meta skill: no gradable output, skip the post-run quality scorer\nmetadata:\n  title: Skill Health\n  category: evolution\n  var: \"\"\n  tags:\n    - meta\n---\n> **${var}** — View selector.\n> - **empty** → health check across all scheduled skills (default).\n> - a **skill slug** (e.g. `token-movers`) → health check for that one skill.\n> - `analytics` or `metrics` (optionally `analytics:HOURS`, e.g. `metrics:72`) → fleet metrics view over the last HOURS (default 168 = 7d, cap 720).\n> - a bare **integer** (e.g. `168`) → metrics view with that window in hours (legacy shorthand).\n\n\u003C!-- autoresearch: variation C — more robust: memory\u002Fissues integration per CLAUDE.md health-skill contract, state-change-gated notifications, graceful missing-data; folds in B's TL;DR+action-directives+top-5 and A's skill-runs fallback. Analytics view absorbed from skill-analytics: ranked fleet view, exit-taxonomy distribution, significance-gated notify + article + dashboard JSON. -->\n\n## Overview\n\nThis skill provides two views over the same GitHub-Actions skill-run data. They share a preamble but branch into distinct logic:\n\n- **health** (default): per-skill classification, issue filing\u002Fresolution against `memory\u002Fissues\u002F`, and a state-change-gated notification. This is the load-bearing self-healing view — its issue contract, `memory\u002Fskill-health\u002F` scoring, and `### skill-health` log shape are depended on by the health loop and other skills. Do not weaken it.\n- **analytics** (metrics): a fleet-wide ranked view — top runners, failure rates, exit-taxonomy distribution, silent-scheduled detection, and anomaly flags — with a significance-gated notification plus an article and a dashboard JSON spec. `heartbeat` gives binary ok\u002Fnot-ok per run and the health view audits skills one degradation-band at a time; the analytics view is the only place the operator sees the entire fleet ranked side-by-side.\n\n## Shared preamble (run for either view)\n\n1. Read `memory\u002FMEMORY.md` for high-level context and scan the last ~3 days of `memory\u002Flogs\u002F` for recent activity — drop anything already reported so you don't re-report the same signal.\n2. Compute `${today}` (UTC date, `YYYY-MM-DD`).\n3. **Parse `${var}` → selector** (trim whitespace first):\n   - **empty** → `VIEW=health`, `TARGET=all` (all scheduled skills).\n   - lowercase first token is `analytics` or `metrics` → `VIEW=analytics`. Parse an optional window argument after a `:` or a space (`analytics:72`, `metrics 336`): if it is a positive integer, `WINDOW_HOURS = min(that, 720)`; otherwise `WINDOW_HOURS = 168`.\n   - a bare positive integer (e.g. `168`) → `VIEW=analytics`, `WINDOW_HOURS = min(that, 720)` (legacy skill-analytics shorthand).\n   - anything else (a non-keyword, non-integer slug) → `VIEW=health`, `TARGET=\u003Cthat slug>` (single-skill health check).\n4. Dispatch: if `VIEW=health`, run **Health view**; if `VIEW=analytics`, run **Analytics view**.\n\n---\n\n# Health view\n\n`VIEW=health`. Audit skill quality metrics, detect API degradation, **file issues for new failures and resolve them when skills recover**, and notify only when fleet health state actually changes. If `TARGET` is a single skill slug, only check that skill.\n\n## Data sources\n\n1. **`memory\u002Fcron-state.json`** — Per-skill quality metrics (as before).\n2. **`memory\u002Fskill-health\u002F*.json`** — Per-skill quality analysis (Haiku post-run).\n3. **`memory\u002Fskill-health\u002Flast-report.json`** — Last run's classification snapshot (this skill writes it). Used to dedup notifications and detect flapping.\n4. **`aeon.yml`** — Enabled skills and schedules.\n5. **`memory\u002Fissues\u002FINDEX.md`** and `memory\u002Fissues\u002FISS-*.md` — Open issues tracker. Check before filing, update on recovery.\n6. **`.\u002Fscripts\u002Fskill-runs --hours 168 --failures --json`** — Fallback source for failures that never wrote to cron-state (runs that crashed before writing, etc.). Run once, parse JSON.\n7. **`memory\u002Flogs\u002FYYYY-MM-DD.md`** (last 3 days) — Grep for `SKILL_*_ERROR` or `EMPTY` signatures keyed to skills missing from skill-health\u002F*.json.\n\n## Steps\n\n### 1. Gather state\n\n- Parse `aeon.yml` → list of enabled skills with schedules. If `TARGET` is a single skill, filter to just that skill.\n- Load `memory\u002Fcron-state.json` (if missing or unparseable, treat as empty — first run, not failure).\n- Load every `memory\u002Fskill-health\u002F*.json` (except `last-report.json`).\n- Load `memory\u002Fskill-health\u002Flast-report.json` if present → `prev_report`. If missing, `prev_report = {}`.\n- Run `.\u002Fscripts\u002Fskill-runs --hours 168 --failures --json 2>\u002Fdev\u002Fnull || echo '{}'` → extract any skill with failures in the last 7d that isn't in cron-state (runs that failed before writing state).\n- Parse `memory\u002Fissues\u002FINDEX.md` → extract open issues with `detected_by: skill-health` and their affected skills. If missing, treat as empty.\n\n### 2. Classify each enabled skill\n\nFor each enabled skill, assign one status using the **first matching rule**:\n\n| Status | Trigger |\n|---|---|\n| **CRITICAL** | `consecutive_failures >= 3` OR (status==failed AND days_since_last_success >= 3) |\n| **DEGRADED** | `success_rate \u003C 0.6` OR (latest `skill-health\u002F*.json` avg_score \u003C 2.5 over ≥3 runs) |\n| **FLAPPING** | 3+ status transitions (success↔failed) in last 7 days per cron-state history *or* `skill-runs` output |\n| **WARNING** | `success_rate \u003C 0.8` OR `consecutive_failures >= 1` |\n| **HEALTHY** | `success_rate >= 0.8` AND `consecutive_failures == 0` AND (no skill-health data OR avg_score >= 3) |\n| **NO DATA** | no entry in cron-state AND never seen in skill-runs |\n\nCompute **severity score** for sorting: `consecutive_failures × (1 + days_since_last_success\u002F7)`. Ties broken by days_since_last_success desc.\n\nFor each CRITICAL\u002FDEGRADED\u002FFLAPPING skill, record:\n- `last_error` (from cron-state or nearest log signature)\n- `api_host` if the error clearly names one (e.g. `api.coingecko.com`, `api.github.com`)\n- `suggested_action` — one of: `FIX CONFIG` (missing secret, bad arg), `WAIT-API` (rate limit, 5xx, timeout on third-party host), `INVESTIGATE` (unrecognised error), `DISPATCH-SKILL` (NO DATA but scheduled — scheduler gap)\n\n### 3. Detect systemic patterns\n\nGroup non-HEALTHY skills by shared `api_host` OR shared `last_error` signature. If ≥2 skills share one:\n- Emit a single `SYSTEMIC:` callout (e.g. `SYSTEMIC: 3 skills failing on api.coingecko.com (rate_limit)`).\n- Do **not** duplicate the same error across per-skill rows — reference the systemic line.\n\n### 4. Reconcile with memory\u002Fissues\u002F\n\n**Precondition guard:** only perform issue filing\u002Fresolution if `memory\u002Fissues\u002FINDEX.md` already exists. If it is missing, the operator has not opted into the issue-tracker contract yet — log `SKILL_HEALTH_ISSUE_TRACKER_MISSING` to `memory\u002Flogs\u002F${today}.md`, skip this entire step (and the reconciliation side of step 5), and continue with classification + notification only. Do **not** auto-create `INDEX.md`.\n\nFor each CRITICAL or FLAPPING skill, check if an issue with `status: open` or `status: fix-pending` already has this skill in `affected_skills` AND a matching `root_cause` signature:\n\n- **Matching issue exists, same root cause** → do nothing (no new file, no notification for this skill).\n- **Matching issue exists, different root cause** → append a note to the existing ISS file's body: `Update YYYY-MM-DD: new signature: \u003Cerror>`. Do not file a new issue.\n- **No matching issue** → file a new one (see below).\n\n**Reconcile `fix-pending` issues first.** `skill-repair` sets `status: fix-pending` with a `fix_pr` when it opens a repair PR - it cannot know whether that PR merges, so it deliberately does not claim `resolved`. This step closes that loop. For each issue with `status: fix-pending` and a non-null `fix_pr`, check the PR's real state (`gh pr view \u003Cfix_pr> --json state,mergedAt`) and reconcile:\n\n- **Merged** (`mergedAt` non-null) → the fix shipped. Set `status: resolved`, `resolved_at: \u003CmergedAt>`, move the row from Open to Resolved in INDEX.md.\n- **Closed without merging** (`state: CLOSED`, `mergedAt: null`) → the fix did not ship. Set `status: open`, set `fix_pr: null`, and append `Update \u003CYYYY-MM-DD>: fix PR \u003Curl> was closed unmerged; issue reopened.` to the body. The skill is still broken; leaving it `fix-pending` would hide that.\n- **Still open** → leave untouched. The repair is in flight.\n\nIf `gh` is unavailable or the lookup errors, leave the issue untouched and log it - never resolve on an unverified assumption.\n\nFor each skill now HEALTHY whose name appears in any `status: open` issue's `affected_skills`:\n- Skip `status: fix-pending` issues. Those wait for the reconcile step above; a lucky HEALTHY classification must not close a repair that has not merged.\n- Remove the skill from that issue's `affected_skills`. If the list becomes empty, set `status: resolved`, set `resolved_at: \u003Cnow ISO>`, and move the row from Open to Resolved in INDEX.md.\n\nFor each skill in a `status: open` **critical** issue that is still DEGRADED or WARNING only because of historical metrics, pipe its cron-state JSON object to `python3 scripts\u002Fskill_health_recovery.py '\u003Cdetected_at>'`. If it prints `recovered`, remove the skill from `affected_skills` and resolve an empty issue exactly as above. A successful run after detection proves that specific failure incident recovered even when lifetime `success_rate` remains low. Do not apply this shortcut to FLAPPING\u002Fhigh issues (one successful run does not prove flapping stopped) or to `status: fix-pending` issues (those wait for the reconcile step above). Invalid or missing state\u002Ftimestamps print `active` and fail closed.\n\n**Filing a new issue:**\n1. Find next ID: scan `memory\u002Fissues\u002FISS-*.md`, take max `NNN`, add 1. Format as zero-padded 3 digits (`ISS-042`).\n2. Write `memory\u002Fissues\u002FISS-NNN.md` with YAML frontmatter:\n   ```yaml\n   ---\n   id: ISS-NNN\n   title: \u003Cskill> \u003Cconcise failure>\n   status: open   # open | fix-pending | resolved. fix-pending = repair PR open, unmerged. Health resolves fix-pending only after merge (or reopens if closed unmerged). HEALTHY recovery still resolves status: open issues with no pending PR.\n   severity: critical | high | medium | low   # critical=CRITICAL status, high=FLAPPING, medium=DEGRADED\n   category: rate-limit | timeout | missing-secret | config | api-change | sandbox-limitation | unknown\n   detected_by: skill-health\n   detected_at: \u003CISO-8601 UTC, e.g. 2026-09-03T12:00:00Z>   # must carry Z or a +00:00 offset; a naive stamp fails closed to active\n   affected_skills: [\u003Cskill>, ...]    # may grow later\n   root_cause: \u003Cerror signature, 1 line>\n   fix_pr: null\n   ---\n   \n   ## What happened\n   \u003C2-3 line summary>\n   \n   ## Signal\n   - consecutive_failures: N\n   - days_since_last_success: N\n   - last_error: \"\u003Cerror>\"\n   - related skills: \u003Clist or \"none\">\n   ```\n3. Append a row to `memory\u002Fissues\u002FINDEX.md` under **Open**: `| ISS-NNN | title | severity | category | YYYY-MM-DD | skill-a, skill-b |`.\n\nAll issue writes must be atomic per file — never partial updates mid-run.\n\n### 5. Decide whether to notify\n\nBuild a stable signature from the current classification: sorted list of `CRITICAL+FLAPPING+DEGRADED skill names + SYSTEMIC callouts`. SHA-256 it → `current_hash`.\n\n- If `current_hash == prev_report.hash` AND `now - prev_report.last_notified_at \u003C 24h` → **do not notify**. State unchanged.\n- Otherwise → **notify** (there's new signal or the daily reminder cadence elapsed).\n\nAlways write `memory\u002Fskill-health\u002Flast-report.json`:\n```json\n{\n  \"hash\": \"\u003Ccurrent_hash>\",\n  \"last_notified_at\": \"\u003CISO if notified this run, else previous value>\",\n  \"last_run_at\": \"\u003CISO now>\",\n  \"classification\": { \"critical\": [...], \"degraded\": [...], \"flapping\": [...], \"warning\": [...], \"healthy_count\": N, \"no_data\": [...] }\n}\n```\n\n### 6. Format the report\n\n**Top line:** `HEALTH: OK` | `HEALTH: WARNING(W)` | `HEALTH: DEGRADED(D)` | `HEALTH: CRITICAL(C)` — most severe wins.\n\n**Body (notify-channel format, max 1 message):**\n\n```\n*Skill Health — ${today}*\nHEALTH: CRITICAL(2)  [systemic: api.coingecko.com rate_limit — 3 skills]\n\n? CRITICAL\n- token-movers — 5 fails, 3d down — WAIT-API (rate_limit) → ISS-042\n- defi-monitor — 4 fails, 2d down — WAIT-API (rate_limit) → ISS-042\n\n? DEGRADED \u002F FLAPPING\n- digest — 52% success (14d), avg quality 2.1 — INVESTIGATE → ISS-043\n\n⚪ NO DATA (2): skill-x, skill-y — DISPATCH-SKILL\n? HEALTHY: 34\n\nOpen issues: 2 · Resolved this run: 1 (rss-digest)\n```\n\nRules for formatting:\n- Cap per-section rows at 5; collapse the rest as `+N more — see memory\u002Fissues\u002FINDEX.md`.\n- Omit HEALTHY list (count only). Omit any empty section.\n- Always end with `Open issues: X · Resolved this run: Y`.\n- If NO CRITICAL\u002FDEGRADED\u002FFLAPPING and no new\u002Fresolved issues → body is just `HEALTH: OK — N skills healthy`.\n\n### 7. Notify and log\n\n- If the gate in step 5 said notify → `.\u002Fnotify \"\u003Creport body>\"`. Update `last_notified_at` in last-report.json to now.\n- If gate said skip → do not call `.\u002Fnotify`. Log to memory\u002Flogs\u002F${today}.md:\n  ```\n  ### skill-health\n  - view: health\n  - SKILL_HEALTH_NOOP — state unchanged since \u003Cprev_run_at>, hash=\u003Cshort>\n  ```\n\nOn notify, log to memory\u002Flogs\u002F${today}.md:\n```\n### skill-health\n- view: health\n- HEALTH: \u003COK|WARNING|DEGRADED|CRITICAL>\n- filed: [ISS-NNN, ...]\n- resolved: [ISS-NNN, ...]\n- open: N\n- systemic: \u003Cpattern or none>\n```\n\nIf all skills healthy, the body-only shortcut from step 6 still fires (once per 24h, per gate) so the operator gets confirmation the audit actually ran — but suppress if last-report.json shows a notify \u003C24h ago with the same OK hash.\n\n## Health-view constraints\n\n- Never file two open issues for the same `(skill, root_cause)` pair — always check INDEX.md first.\n- Never edit a Resolved issue. If a previously-resolved issue re-fires, file a new ISS with a pointer (`related: ISS-NNN`) in the body.\n- Do not notify on pure HEALTHY runs more than once per 24h.\n- If in single-skill mode (selector was a skill slug), skip INDEX.md updates only if the single skill is HEALTHY — otherwise file\u002Fresolve as normal.\n- Never touch `memory\u002Fissues\u002FINDEX.md` Resolved section except to move rows into it; never delete rows.\n\n---\n\n# Analytics view\n\n`VIEW=analytics`. Generate a fleet-level performance view of every Aeon skill that has run in the window. **The point of this view is to answer four questions in one report:** which skills run most, which fail most, which are silently skipping (new exit taxonomy from the autoresearch-evolution rewrites), and which scheduled skills haven't fired at all.\n\n## Why this exists\n\n`heartbeat` runs daily and emits a per-skill ✓\u002F✗. The health view (above) files issues for skills that breach degradation thresholds. Neither produces a ranked, fleet-wide view. The 80 autoresearch-evolution rewrites (aeon PRs #46–#136) introduced new exit taxonomies — `SKIP_UNCHANGED`, `NEW_INFO`, `SKIP_QUIET` — that classify quiet-but-correct runs separately from failures. Existing health checks treat any non-`*_OK` exit as worth attention; the analytics view makes the actual distribution visible so a skill running mostly `SKIP_UNCHANGED` reads as healthy-quiet, not silently broken.\n\n## Steps\n\n### 1. Determine the window\n\n- `WINDOW_HOURS` was set by the selector parse (default 168 = 7 days; a positive integer from `analytics:N`, `metrics N`, or a bare integer; capped at 720 = 30 days — anything longer slows the `gh api` paginate).\n- Compute `WINDOW_LABEL` (e.g. `\"last 7d\"` or `\"last 72h\"`).\n\n### 2. Pull the run snapshot\n\nWith WINDOW_HOURS written in literally. A shell `>` redirect into a file is refused in this run, so the JSON reaches its file through a piped one-line `node` writer:\n```bash\n.\u002Fscripts\u002Fskill-runs --json --hours \u003CWINDOW_HOURS> 2>\u002Fdev\u002Fnull | node -e 'require(\"fs\").writeFileSync(\"output\u002F.chains\u002Fskill-analytics-runs.json\", require(\"fs\").readFileSync(0))'\n```\n\nIf the script fails (auth, rate limit, network error) or the JSON is empty:\n- Log `SKILL_ANALYTICS_NO_DATA — skill-runs returned empty (gh api \u002F network error?)` to `memory\u002Flogs\u002F${today}.md` (under the `### skill-health` heading, see step 13) and stop with **no notification**. A silent fleet view is correct on data-fetch failure — fall back rather than guess.\n\nThe script's JSON shape (see `scripts\u002Fskill-runs`):\n```json\n{\n  \"period\": {\"since\": \"...\", \"until\": \"...\", \"hours\": 168},\n  \"summary\": {\"total\": N, \"succeeded\": N, \"failed\": N, \"cancelled\": N, \"in_progress\": N},\n  \"skills\": [{\"skill\": \"name\", \"total\": N, \"success\": N, \"failure\": N, \"cancelled\": N, \"in_progress\": N, \"last_run\": \"...\", \"last_conclusion\": \"...\"}],\n  \"anomalies\": {\"duplicates\": [...], \"failing\": [...]}\n}\n```\n\n### 3. Cross-reference with cron schedule\n\nRead `aeon.yml` and build `SCHEDULED_SKILLS`: dict `{skill_name -> {enabled: bool, schedule: str}}` for every entry under `skills:`. Treat `schedule: \"workflow_dispatch\"` and `schedule: \"reactive\"` as exempt from the \"no runs in window\" anomaly — those are dispatched on demand, not by cron.\n\nFor every skill in `SCHEDULED_SKILLS` where `enabled: true` AND schedule is a valid cron expression AND the skill is **not** present in the snapshot's `skills` array, mark `silent_scheduled: true` (zero runs in window despite an active schedule).\n\n### 4. Cross-reference with cron-state.json\n\nLoad `memory\u002Fcron-state.json` if present (missing → empty dict, not failure). For each skill in the snapshot, attach:\n- `consecutive_failures` (0 if missing)\n- `last_status` (`\"unknown\"` if missing)\n\nUsed to compute the consecutive-failure anomaly without a second `gh api` round-trip.\n\n### 5. Mine exit taxonomy from logs\n\nFor each daily log file `memory\u002Flogs\u002FYYYY-MM-DD.md` whose date falls in the window, scan for these markers (one match per skill section):\n- `_OK` → success (excluding `_OK_SILENT`)\n- `_OK_SILENT` \u002F `_QUIET` \u002F `SKIP_QUIET` → quiet-success\n- `SKIP_UNCHANGED` → skip-unchanged (autoresearch-evolution exit)\n- `NEW_INFO` → new-info (autoresearch-evolution exit)\n- `_SKIP*` (other) → skip-other\n- `_ERROR` \u002F `_FAILED` → error\n- `_PARTIAL` → partial\n- (no match) → uncategorized\n\nBuild `EXIT_DIST[skill]` = `{ok: N, quiet: N, skip_unchanged: N, new_info: N, skip_other: N, error: N, partial: N, uncategorized: N}`. The dominant bucket per skill is the one with the largest count; ties broken in the order listed above. If a skill has no log markers in the window, dominant bucket is `\"uncategorized\"`.\n\nThis step is best-effort — the markers are regex-grepped from human-written logs, not parsed from a contract. A miss-rate of 10–20% is expected and acceptable; the GitHub Actions success\u002Ffailure counts from step 2 remain the ground truth for pass\u002Ffail. The taxonomy distribution is a secondary signal.\n\n### 6. Anomaly classification\n\nFor each skill in the snapshot OR `silent_scheduled`, assign **at most one** anomaly flag, first match wins:\n\n| Flag | Trigger |\n|---|---|\n| `? SILENT` | `silent_scheduled: true` (enabled cron skill, zero runs in window) |\n| `? ALL_FAIL` | `total >= 2` AND `failure == total` |\n| `? CONSECUTIVE_FAILURES` | `consecutive_failures >= 3` (from cron-state) |\n| `? LOW_SUCCESS` | `total >= 3` AND `success \u002F total \u003C 0.80` |\n| `? ALL_SKIP` | `total >= 3` AND `EXIT_DIST.ok + EXIT_DIST.quiet + EXIT_DIST.new_info == 0` AND `EXIT_DIST.skip_unchanged + EXIT_DIST.skip_other > 0` (every run skipped — possibly correct, possibly stuck) |\n| `? DUPLICATE_RUNS` | `total > 2 × expected_runs(schedule, window)` (more runs than the cron should produce — manual reruns or scheduler glitch) |\n\n`expected_runs(schedule, window)` is a coarse estimate — for a cron `\"0 H * * *\"` over 7 days, expect 7; for `\"0 H,H,H * * *\"`, expect 21; for weekly `\"0 H * * D\"`, expect 1. If the schedule string is unparseable, skip the duplicate check for that skill (do not flag false positives).\n\nA skill with no flag is considered HEALTHY for analytics purposes.\n\n### 7. Compute summary\n\n```\ntotal_runs:          sum of every skill's total\ndistinct_skills:     count of skills with total >= 1\noverall_success_pct: snapshot.summary.succeeded \u002F (succeeded + failed) × 100  (cancelled + in_progress excluded)\nanomaly_count:       count of skills with any flag in step 6\nsilent_scheduled_count: count of SILENT flags\nexit_dominant:       top 3 dominant exit buckets across the fleet, e.g. \"ok (42), skip_unchanged (18), error (3)\"\n```\n\n### 8. Build the verdict line\n\nPick the strongest single claim, in priority:\n\n1. Any `? SILENT` exists → `\"${N} scheduled skill(s) didn't run this window — ${first_skill}\"`\n2. Any `? ALL_FAIL` exists → `\"${first_skill} failed every run (${N}\u002F${N}) — investigate\"`\n3. Any `? CONSECUTIVE_FAILURES` exists → `\"${first_skill} on ${N}-run failure streak\"`\n4. Any `? LOW_SUCCESS` exists → `\"${first_skill} ${pct}% success over ${total} runs — degraded\"`\n5. Any `? ALL_SKIP` exists → `\"${N} skill(s) only emitting skip-class exits this window — verify intent\"`\n6. Otherwise → `\"All ${distinct_skills} active skills healthy — ${overall_success_pct}% success across ${total_runs} runs\"`\n\n### 9. Significance gate\n\n**Notify only if `anomaly_count >= 1`.** Silent run = correct (no anomalies in fleet) = no notification. Following the autoresearch-evolution \u002F fork-digest pattern: noisy skills break trust faster than missing pings.\n\nIf gate says skip, still write the article and JSON spec, and log `SKILL_ANALYTICS_QUIET` (no anomalies). The dashboard widget refreshes regardless; only the push notification is gated.\n\n### 10. Write the article\n\nPath: `output\u002Farticles\u002Fskill-analytics-${today}.md`. Overwrite if it exists (idempotent same-day reruns).\n\n```markdown\n# Skill Analytics — ${today}\n\n**Verdict:** ${verdict_line}\n\n*Window: ${WINDOW_LABEL} · ${total_runs} runs across ${distinct_skills} skills · ${overall_success_pct}% success · ${anomaly_count} anomalies*\n\n## Anomalies\n\n| Flag | Skill | Detail | Action |\n|------|-------|--------|--------|\n| ? SILENT | name | scheduled `\u003Ccron>` but zero runs in window | run **aeon-doctor** — zero-runs-despite-schedule is usually a static config bug (unquoted `schedule:`, dup key) it can't be seen in run data |\n| ? ALL_FAIL | name | N\u002FN failed | investigate root cause |\n| ? CONSECUTIVE_FAILURES | name | N-run streak (last_error: \"...\") | see health view for filed issue |\n| ? LOW_SUCCESS | name | N% over M runs | review failures |\n| ? ALL_SKIP | name | M runs, all skip-class | confirm SKIP_UNCHANGED is the intent |\n| ? DUPLICATE_RUNS | name | M runs, expected ~K | check for manual reruns |\n\n(If `anomaly_count == 0`: write `No anomalies — fleet healthy across ${distinct_skills} skills.`)\n\n## Top runners (by run count)\n\n| # | Skill | Runs | Success | Last status | Dominant exit |\n|---|-------|------|---------|-------------|---------------|\n| 1 | name  | N    | XX%     | success     | ok            |\n| 2 | name  | N    | XX%     | success     | skip_unchanged |\n...\n\n(Top 15 by total runs desc. If fewer than 15 active skills, list all.)\n\n## Failure rate (sorted, ≥1 failure)\n\n| Skill | Runs | Failures | Success rate | Last conclusion |\n|-------|------|----------|--------------|-----------------|\n\n(All skills with `failure >= 1`, sorted by `failure \u002F total` desc. If none: \"Zero failures across ${distinct_skills} skills this window.\")\n\n## Exit taxonomy distribution\n\n| Bucket | Count | % | Top skills |\n|--------|-------|---|------------|\n| ok            | N | XX% | a, b, c |\n| skip_unchanged | N | XX% | d, e |\n| new_info      | N | XX% | f |\n| quiet         | N | XX% | g |\n| error         | N | XX% | h |\n| partial       | N | XX% |   |\n| uncategorized | N | XX% |   |\n\n(Sourced from `memory\u002Flogs\u002F*.md` — best-effort regex grep, see Step 5. Cell-aligns to summary cells above where available.)\n\n## Silent scheduled skills (enabled, zero runs)\n\n${list of {skill, schedule} pairs OR \"none — every enabled cron skill ran at least once.\"}\n\n> **Root-cause pointer:** an enabled cron skill with *zero* runs is most often a **static config** fault the run data can't reveal — an unquoted `schedule:` the scheduler regex silently skips, a duplicate `aeon.yml` key, or an entry pointing at a missing `SKILL.md`. That class is `aeon-doctor`'s job; dispatch it (`.\u002Faeon skills run aeon-doctor`) to pinpoint the cause instead of guessing at the scheduler.\n\n## Source status\n\n- skill-runs JSON: ${ok|empty|fetch_error}\n- Window: ${WINDOW_HOURS}h (${period.since} → ${period.until})\n- aeon.yml: ${ok|missing}\n- cron-state.json: ${ok|missing — first run for this fork?}\n- Daily logs scanned: ${N_LOG_FILES}\u002F${expected_log_files} for exit taxonomy\n\n---\n*The analytics view of `skill-health` (per-skill issue filing lives in the health view) and a companion to `heartbeat` (per-run pulse). Fleet-wide observability is the gap this view closes. Methodology: GitHub Actions run history is ground truth for pass\u002Ffail; daily-log markers are best-effort secondary signal for exit taxonomy.*\n```\n\n### 11. Write the dashboard JSON spec\n\nPath: `apps\u002Fdashboard\u002Foutputs\u002Fskill-analytics.json`. Use the catalog components (Card \u002F Stack \u002F Heading \u002F Text \u002F Badge \u002F Table).\n\n```json\n{\n  \"version\": \"1\",\n  \"generated_at\": \"${ISO timestamp}\",\n  \"skill\": \"skill-analytics\",\n  \"title\": \"Skill Analytics — ${today}\",\n  \"spec\": {\n    \"type\": \"Stack\",\n    \"props\": {\"direction\": \"vertical\", \"gap\": \"md\"},\n    \"children\": [\n      {\"type\": \"Heading\", \"props\": {\"level\": 2, \"children\": \"Skill Analytics — ${today}\"}},\n      {\"type\": \"Text\", \"props\": {\"variant\": \"muted\", \"children\": \"${verdict_line}\"}},\n      {\"type\": \"Grid\", \"props\": {\"columns\": 4, \"gap\": \"sm\"}, \"children\": [\n        {\"type\": \"Card\", \"props\": {\"children\": [\n          {\"type\": \"Text\", \"props\": {\"variant\": \"muted\", \"children\": \"Total runs\"}},\n          {\"type\": \"Heading\", \"props\": {\"level\": 3, \"children\": \"${total_runs}\"}}\n        ]}},\n        {\"type\": \"Card\", \"props\": {\"children\": [\n          {\"type\": \"Text\", \"props\": {\"variant\": \"muted\", \"children\": \"Active skills\"}},\n          {\"type\": \"Heading\", \"props\": {\"level\": 3, \"children\": \"${distinct_skills}\"}}\n        ]}},\n        {\"type\": \"Card\", \"props\": {\"children\": [\n          {\"type\": \"Text\", \"props\": {\"variant\": \"muted\", \"children\": \"Success rate\"}},\n          {\"type\": \"Heading\", \"props\": {\"level\": 3, \"children\": \"${overall_success_pct}%\"}}\n        ]}},\n        {\"type\": \"Card\", \"props\": {\"children\": [\n          {\"type\": \"Text\", \"props\": {\"variant\": \"muted\", \"children\": \"Anomalies\"}},\n          {\"type\": \"Heading\", \"props\": {\"level\": 3, \"children\": \"${anomaly_count}\"}}\n        ]}}\n      ]},\n      {\"type\": \"Heading\", \"props\": {\"level\": 3, \"children\": \"Top runners\"}},\n      {\"type\": \"Table\", \"props\": {\n        \"columns\": [\n          {\"key\": \"rank\", \"header\": \"#\"},\n          {\"key\": \"skill\", \"header\": \"Skill\"},\n          {\"key\": \"runs\", \"header\": \"Runs\"},\n          {\"key\": \"success\", \"header\": \"Success\"},\n          {\"key\": \"exit\", \"header\": \"Dominant exit\"}\n        ],\n        \"rows\": [\n          {\"rank\": \"1\", \"skill\": \"name\", \"runs\": \"N\", \"success\": \"XX%\", \"exit\": \"ok\"}\n        ]\n      }}\n    ]\n  }\n}\n```\n\nIf `anomaly_count >= 1`, prepend an `Alert` block before the verdict:\n\n```json\n{\"type\": \"Alert\", \"props\": {\"variant\": \"destructive\", \"children\": \"${anomaly_count} anomaly flag(s) raised — see Anomalies section\"}}\n```\n\nIf the file write fails (filesystem read-only, missing directory), log a warning but do not abort — the article is the canonical artifact, the JSON spec is a dashboard convenience.\n\n### 12. Send notification (only if gate from step 9 passed)\n\nVia `.\u002Fnotify`:\n\n```\n*Skill Analytics — ${today}*\n${verdict_line}\n\nWindow: ${WINDOW_LABEL} · ${total_runs} runs · ${distinct_skills} skills · ${overall_success_pct}% success\nAnomalies: ${anomaly_count}\n\n${If ? flags (cap top 3):}\n? Critical:\n- ${skill} — ${flag}: ${detail}\n\n${If ? flags (cap top 3):}\n? Degraded:\n- ${skill} — ${flag}: ${detail}\n\n${If ? flags (top 3, only if no ?\u002F? already filled the slots):}\n? Watch:\n- ${skill} — ${flag}: ${detail}\n\nTop by runs: ${top_3_skills_by_run_count_with_counts}\n\nFull: output\u002Farticles\u002Fskill-analytics-${today}.md\n```\n\nKeep the message body tight for signal. Drop the \"Top by runs\" line first if it runs long; flags are higher signal. (`.\u002Fnotify` auto-chunks, so length is about signal, not transport.)\n\n### 13. Log to `memory\u002Flogs\u002F${today}.md`\n\nLog under the shared `### skill-health` heading (the health loop parses this shape), with a `view: analytics` discriminator:\n\n```\n### skill-health\n- view: analytics\n- **Window**: ${WINDOW_LABEL} (${WINDOW_HOURS}h)\n- **Total runs**: ${total_runs} across ${distinct_skills} skills\n- **Overall success rate**: ${overall_success_pct}%\n- **Anomalies**: ${anomaly_count} (? ${red_count}, ? ${orange_count}, ? ${yellow_count})\n- **Silent scheduled**: ${silent_scheduled_count} skills (${comma list capped at 5})\n- **Top runner**: ${top_skill} (${top_runs} runs)\n- **Exit dominant**: ${exit_dominant_summary}\n- **Verdict**: ${verdict_line}\n- **Article**: output\u002Farticles\u002Fskill-analytics-${today}.md\n- **Dashboard**: apps\u002Fdashboard\u002Foutputs\u002Fskill-analytics.json\n- **Notification sent**: ${yes|no — quiet (no anomalies)}\n- **Status**: SKILL_ANALYTICS_OK | SKILL_ANALYTICS_QUIET | SKILL_ANALYTICS_NO_DATA\n```\n\n## Analytics-view exit taxonomy\n\n| Status | Meaning | Notify? |\n|--------|---------|---------|\n| `SKILL_ANALYTICS_OK` | snapshot fetched, ≥1 anomaly flagged | Yes |\n| `SKILL_ANALYTICS_QUIET` | snapshot fetched, zero anomalies | No (article + JSON written, log only) |\n| `SKILL_ANALYTICS_NO_DATA` | skill-runs returned empty \u002F fetch failed | No (log only, no article overwrite) |\n\n## Analytics-view constraints\n\n- **Significance-gated.** A clean fleet must produce zero notifications. Article and JSON spec still write so the dashboard reflects the latest state, but `.\u002Fnotify` is silent.\n- **Never invent runs.** If `skill-runs` returns empty, exit `SKILL_ANALYTICS_NO_DATA` — do not synthesise data from cron-state alone (cron-state's view is per-skill, not chronologically ordered, and would produce a misleading \"top runners\" table).\n- **Best-effort exit-taxonomy parsing.** Log markers are human-written; expect a 10–20% miss rate. Do not block the article on parse failures — drop the affected skill into `uncategorized` and continue.\n- **Idempotent.** Same-day reruns overwrite the article and JSON spec. The log entry is appended (one block per run, lets the operator see analytic drift across reruns).\n- **No issue filing in this view.** The analytics view does not write to `memory\u002Fissues\u002F` — that contract belongs to the health view. Anomalies surface here as flags; persistence and resolution live in the health view's domain.\n- **Respect workflow_dispatch \u002F reactive.** Skills with non-cron schedules cannot be SILENT — they fire only on demand. Excluding them from the silent-scheduled check prevents permanent false positives.\n\n---\n\n## Network note (both views)\n\nThis skill fetches no URLs directly — all data is local or via `gh` \u002F `.\u002Fscripts\u002Fskill-runs` (which uses `gh api`, so auth comes from `GITHUB_TOKEN` with no secret ever on the command line). No `curl` fallback needed.\n\n- **Health view:** if `.\u002Fscripts\u002Fskill-runs` fails, log `SKILL_HEALTH_PARTIAL — skill-runs unavailable` and continue with cron-state only.\n- **Analytics view:** if `gh api` is rate-limited or the runner's network is degraded, `.\u002Fscripts\u002Fskill-runs` exits non-zero; catch that and fall through to `SKILL_ANALYTICS_NO_DATA` rather than emitting a partial fleet view that would mislead.\n","> **${var}** — View selector.\n> - **empty** → health check across all scheduled skills (default).\n> - a **skill slug** (e.g. `token-movers`) → health check for that one skill.\n> - `analytics` or `metrics` (optionally `analytics:HOURS`, e.g. `metrics:72`) → fleet metrics view over the last HOURS (default 168 = 7d, cap 720).\n> - a bare **integer** (e.g. `168`) → metrics view with that window in hours (legacy shorthand).\n\n\u003C!-- autoresearch: variation C — more robust: memory\u002Fissues integration per CLAUDE.md health-skill contract, state-change-gated notifications, graceful missing-data; folds in B's TL;DR+action-directives+top-5 and A's skill-runs fallback. Analytics view absorbed from skill-analytics: ranked fleet view, exit-taxonomy distribution, significance-gated notify + article + dashboard JSON. -->\n\n## Overview\n\nThis skill provides two views over the same GitHub-Actions skill-run data. They share a preamble but branch into distinct logic:\n\n- **health** (default): per-skill classification, issue filing\u002Fresolution against `memory\u002Fissues\u002F`, and a state-change-gated notification. This is the load-bearing self-healing view — its issue contract, `memory\u002Fskill-health\u002F` scoring, and `### skill-health` log shape are depended on by the health loop and other skills. Do not weaken it.\n- **analytics** (metrics): a fleet-wide ranked view — top runners, failure rates, exit-taxonomy distribution, silent-scheduled detection, and anomaly flags — with a significance-gated notification plus an article and a dashboard JSON spec. `heartbeat` gives binary ok\u002Fnot-ok per run and the health view audits skills one degradation-band at a time; the analytics view is the only place the operator sees the entire fleet ranked side-by-side.\n\n## Shared preamble (run for either view)\n\n1. Read `memory\u002FMEMORY.md` for high-level context and scan the last ~3 days of `memory\u002Flogs\u002F` for recent activity — drop anything already reported so you don't re-report the same signal.\n2. Compute `${today}` (UTC date, `YYYY-MM-DD`).\n3. **Parse `${var}` → selector** (trim whitespace first):\n   - **empty** → `VIEW=health`, `TARGET=all` (all scheduled skills).\n   - lowercase first token is `analytics` or `metrics` → `VIEW=analytics`. Parse an optional window argument after a `:` or a space (`analytics:72`, `metrics 336`): if it is a positive integer, `WINDOW_HOURS = min(that, 720)`; otherwise `WINDOW_HOURS = 168`.\n   - a bare positive integer (e.g. `168`) → `VIEW=analytics`, `WINDOW_HOURS = min(that, 720)` (legacy skill-analytics shorthand).\n   - anything else (a non-keyword, non-integer slug) → `VIEW=health`, `TARGET=\u003Cthat slug>` (single-skill health check).\n4. Dispatch: if `VIEW=health`, run **Health view**; if `VIEW=analytics`, run **Analytics view**.\n\n---\n\n# Health view\n\n`VIEW=health`. Audit skill quality metrics, detect API degradation, **file issues for new failures and resolve them when skills recover**, and notify only when fleet health state actually changes. If `TARGET` is a single skill slug, only check that skill.\n\n## Data sources\n\n1. **`memory\u002Fcron-state.json`** — Per-skill quality metrics (as before).\n2. **`memory\u002Fskill-health\u002F*.json`** — Per-skill quality analysis (Haiku post-run).\n3. **`memory\u002Fskill-health\u002Flast-report.json`** — Last run's classification snapshot (this skill writes it). Used to dedup notifications and detect flapping.\n4. **`aeon.yml`** — Enabled skills and schedules.\n5. **`memory\u002Fissues\u002FINDEX.md`** and `memory\u002Fissues\u002FISS-*.md` — Open issues tracker. Check before filing, update on recovery.\n6. **`.\u002Fscripts\u002Fskill-runs --hours 168 --failures --json`** — Fallback source for failures that never wrote to cron-state (runs that crashed before writing, etc.). Run once, parse JSON.\n7. **`memory\u002Flogs\u002FYYYY-MM-DD.md`** (last 3 days) — Grep for `SKILL_*_ERROR` or `EMPTY` signatures keyed to skills missing from skill-health\u002F*.json.\n\n## Steps\n\n### 1. Gather state\n\n- Parse `aeon.yml` → list of enabled skills with schedules. If `TARGET` is a single skill, filter to just that skill.\n- Load `memory\u002Fcron-state.json` (if missing or unparseable, treat as empty — first run, not failure).\n- Load every `memory\u002Fskill-health\u002F*.json` (except `last-report.json`).\n- Load `memory\u002Fskill-health\u002Flast-report.json` if present → `prev_report`. If missing, `prev_report = {}`.\n- Run `.\u002Fscripts\u002Fskill-runs --hours 168 --failures --json 2>\u002Fdev\u002Fnull || echo '{}'` → extract any skill with failures in the last 7d that isn't in cron-state (runs that failed before writing state).\n- Parse `memory\u002Fissues\u002FINDEX.md` → extract open issues with `detected_by: skill-health` and their affected skills. If missing, treat as empty.\n\n### 2. Classify each enabled skill\n\nFor each enabled skill, assign one status using the **first matching rule**:\n\n| Status | Trigger |\n|---|---|\n| **CRITICAL** | `consecutive_failures >= 3` OR (status==failed AND days_since_last_success >= 3) |\n| **DEGRADED** | `success_rate \u003C 0.6` OR (latest `skill-health\u002F*.json` avg_score \u003C 2.5 over ≥3 runs) |\n| **FLAPPING** | 3+ status transitions (success↔failed) in last 7 days per cron-state history *or* `skill-runs` output |\n| **WARNING** | `success_rate \u003C 0.8` OR `consecutive_failures >= 1` |\n| **HEALTHY** | `success_rate >= 0.8` AND `consecutive_failures == 0` AND (no skill-health data OR avg_score >= 3) |\n| **NO DATA** | no entry in cron-state AND never seen in skill-runs |\n\nCompute **severity score** for sorting: `consecutive_failures × (1 + days_since_last_success\u002F7)`. Ties broken by days_since_last_success desc.\n\nFor each CRITICAL\u002FDEGRADED\u002FFLAPPING skill, record:\n- `last_error` (from cron-state or nearest log signature)\n- `api_host` if the error clearly names one (e.g. `api.coingecko.com`, `api.github.com`)\n- `suggested_action` — one of: `FIX CONFIG` (missing secret, bad arg), `WAIT-API` (rate limit, 5xx, timeout on third-party host), `INVESTIGATE` (unrecognised error), `DISPATCH-SKILL` (NO DATA but scheduled — scheduler gap)\n\n### 3. Detect systemic patterns\n\nGroup non-HEALTHY skills by shared `api_host` OR shared `last_error` signature. If ≥2 skills share one:\n- Emit a single `SYSTEMIC:` callout (e.g. `SYSTEMIC: 3 skills failing on api.coingecko.com (rate_limit)`).\n- Do **not** duplicate the same error across per-skill rows — reference the systemic line.\n\n### 4. Reconcile with memory\u002Fissues\u002F\n\n**Precondition guard:** only perform issue filing\u002Fresolution if `memory\u002Fissues\u002FINDEX.md` already exists. If it is missing, the operator has not opted into the issue-tracker contract yet — log `SKILL_HEALTH_ISSUE_TRACKER_MISSING` to `memory\u002Flogs\u002F${today}.md`, skip this entire step (and the reconciliation side of step 5), and continue with classification + notification only. Do **not** auto-create `INDEX.md`.\n\nFor each CRITICAL or FLAPPING skill, check if an issue with `status: open` or `status: fix-pending` already has this skill in `affected_skills` AND a matching `root_cause` signature:\n\n- **Matching issue exists, same root cause** → do nothing (no new file, no notification for this skill).\n- **Matching issue exists, different root cause** → append a note to the existing ISS file's body: `Update YYYY-MM-DD: new signature: \u003Cerror>`. Do not file a new issue.\n- **No matching issue** → file a new one (see below).\n\n**Reconcile `fix-pending` issues first.** `skill-repair` sets `status: fix-pending` with a `fix_pr` when it opens a repair PR - it cannot know whether that PR merges, so it deliberately does not claim `resolved`. This step closes that loop. For each issue with `status: fix-pending` and a non-null `fix_pr`, check the PR's real state (`gh pr view \u003Cfix_pr> --json state,mergedAt`) and reconcile:\n\n- **Merged** (`mergedAt` non-null) → the fix shipped. Set `status: resolved`, `resolved_at: \u003CmergedAt>`, move the row from Open to Resolved in INDEX.md.\n- **Closed without merging** (`state: CLOSED`, `mergedAt: null`) → the fix did not ship. Set `status: open`, set `fix_pr: null`, and append `Update \u003CYYYY-MM-DD>: fix PR \u003Curl> was closed unmerged; issue reopened.` to the body. The skill is still broken; leaving it `fix-pending` would hide that.\n- **Still open** → leave untouched. The repair is in flight.\n\nIf `gh` is unavailable or the lookup errors, leave the issue untouched and log it - never resolve on an unverified assumption.\n\nFor each skill now HEALTHY whose name appears in any `status: open` issue's `affected_skills`:\n- Skip `status: fix-pending` issues. Those wait for the reconcile step above; a lucky HEALTHY classification must not close a repair that has not merged.\n- Remove the skill from that issue's `affected_skills`. If the list becomes empty, set `status: resolved`, set `resolved_at: \u003Cnow ISO>`, and move the row from Open to Resolved in INDEX.md.\n\nFor each skill in a `status: open` **critical** issue that is still DEGRADED or WARNING only because of historical metrics, pipe its cron-state JSON object to `python3 scripts\u002Fskill_health_recovery.py '\u003Cdetected_at>'`. If it prints `recovered`, remove the skill from `affected_skills` and resolve an empty issue exactly as above. A successful run after detection proves that specific failure incident recovered even when lifetime `success_rate` remains low. Do not apply this shortcut to FLAPPING\u002Fhigh issues (one successful run does not prove flapping stopped) or to `status: fix-pending` issues (those wait for the reconcile step above). Invalid or missing state\u002Ftimestamps print `active` and fail closed.\n\n**Filing a new issue:**\n1. Find next ID: scan `memory\u002Fissues\u002FISS-*.md`, take max `NNN`, add 1. Format as zero-padded 3 digits (`ISS-042`).\n2. Write `memory\u002Fissues\u002FISS-NNN.md` with YAML frontmatter:\n   ```yaml\n   ---\n   id: ISS-NNN\n   title: \u003Cskill> \u003Cconcise failure>\n   status: open   # open | fix-pending | resolved. fix-pending = repair PR open, unmerged. Health resolves fix-pending only after merge (or reopens if closed unmerged). HEALTHY recovery still resolves status: open issues with no pending PR.\n   severity: critical | high | medium | low   # critical=CRITICAL status, high=FLAPPING, medium=DEGRADED\n   category: rate-limit | timeout | missing-secret | config | api-change | sandbox-limitation | unknown\n   detected_by: skill-health\n   detected_at: \u003CISO-8601 UTC, e.g. 2026-09-03T12:00:00Z>   # must carry Z or a +00:00 offset; a naive stamp fails closed to active\n   affected_skills: [\u003Cskill>, ...]    # may grow later\n   root_cause: \u003Cerror signature, 1 line>\n   fix_pr: null\n   ---\n   \n   ## What happened\n   \u003C2-3 line summary>\n   \n   ## Signal\n   - consecutive_failures: N\n   - days_since_last_success: N\n   - last_error: \"\u003Cerror>\"\n   - related skills: \u003Clist or \"none\">\n   ```\n3. Append a row to `memory\u002Fissues\u002FINDEX.md` under **Open**: `| ISS-NNN | title | severity | category | YYYY-MM-DD | skill-a, skill-b |`.\n\nAll issue writes must be atomic per file — never partial updates mid-run.\n\n### 5. Decide whether to notify\n\nBuild a stable signature from the current classification: sorted list of `CRITICAL+FLAPPING+DEGRADED skill names + SYSTEMIC callouts`. SHA-256 it → `current_hash`.\n\n- If `current_hash == prev_report.hash` AND `now - prev_report.last_notified_at \u003C 24h` → **do not notify**. State unchanged.\n- Otherwise → **notify** (there's new signal or the daily reminder cadence elapsed).\n\nAlways write `memory\u002Fskill-health\u002Flast-report.json`:\n```json\n{\n  \"hash\": \"\u003Ccurrent_hash>\",\n  \"last_notified_at\": \"\u003CISO if notified this run, else previous value>\",\n  \"last_run_at\": \"\u003CISO now>\",\n  \"classification\": { \"critical\": [...], \"degraded\": [...], \"flapping\": [...], \"warning\": [...], \"healthy_count\": N, \"no_data\": [...] }\n}\n```\n\n### 6. Format the report\n\n**Top line:** `HEALTH: OK` | `HEALTH: WARNING(W)` | `HEALTH: DEGRADED(D)` | `HEALTH: CRITICAL(C)` — most severe wins.\n\n**Body (notify-channel format, max 1 message):**\n\n```\n*Skill Health — ${today}*\nHEALTH: CRITICAL(2)  [systemic: api.coingecko.com rate_limit — 3 skills]\n\n? CRITICAL\n- token-movers — 5 fails, 3d down — WAIT-API (rate_limit) → ISS-042\n- defi-monitor — 4 fails, 2d down — WAIT-API (rate_limit) → ISS-042\n\n? DEGRADED \u002F FLAPPING\n- digest — 52% success (14d), avg quality 2.1 — INVESTIGATE → ISS-043\n\n⚪ NO DATA (2): skill-x, skill-y — DISPATCH-SKILL\n? HEALTHY: 34\n\nOpen issues: 2 · Resolved this run: 1 (rss-digest)\n```\n\nRules for formatting:\n- Cap per-section rows at 5; collapse the rest as `+N more — see memory\u002Fissues\u002FINDEX.md`.\n- Omit HEALTHY list (count only). Omit any empty section.\n- Always end with `Open issues: X · Resolved this run: Y`.\n- If NO CRITICAL\u002FDEGRADED\u002FFLAPPING and no new\u002Fresolved issues → body is just `HEALTH: OK — N skills healthy`.\n\n### 7. Notify and log\n\n- If the gate in step 5 said notify → `.\u002Fnotify \"\u003Creport body>\"`. Update `last_notified_at` in last-report.json to now.\n- If gate said skip → do not call `.\u002Fnotify`. Log to memory\u002Flogs\u002F${today}.md:\n  ```\n  ### skill-health\n  - view: health\n  - SKILL_HEALTH_NOOP — state unchanged since \u003Cprev_run_at>, hash=\u003Cshort>\n  ```\n\nOn notify, log to memory\u002Flogs\u002F${today}.md:\n```\n### skill-health\n- view: health\n- HEALTH: \u003COK|WARNING|DEGRADED|CRITICAL>\n- filed: [ISS-NNN, ...]\n- resolved: [ISS-NNN, ...]\n- open: N\n- systemic: \u003Cpattern or none>\n```\n\nIf all skills healthy, the body-only shortcut from step 6 still fires (once per 24h, per gate) so the operator gets confirmation the audit actually ran — but suppress if last-report.json shows a notify \u003C24h ago with the same OK hash.\n\n## Health-view constraints\n\n- Never file two open issues for the same `(skill, root_cause)` pair — always check INDEX.md first.\n- Never edit a Resolved issue. If a previously-resolved issue re-fires, file a new ISS with a pointer (`related: ISS-NNN`) in the body.\n- Do not notify on pure HEALTHY runs more than once per 24h.\n- If in single-skill mode (selector was a skill slug), skip INDEX.md updates only if the single skill is HEALTHY — otherwise file\u002Fresolve as normal.\n- Never touch `memory\u002Fissues\u002FINDEX.md` Resolved section except to move rows into it; never delete rows.\n\n---\n\n# Analytics view\n\n`VIEW=analytics`. Generate a fleet-level performance view of every Aeon skill that has run in the window. **The point of this view is to answer four questions in one report:** which skills run most, which fail most, which are silently skipping (new exit taxonomy from the autoresearch-evolution rewrites), and which scheduled skills haven't fired at all.\n\n## Why this exists\n\n`heartbeat` runs daily and emits a per-skill ✓\u002F✗. The health view (above) files issues for skills that breach degradation thresholds. Neither produces a ranked, fleet-wide view. The 80 autoresearch-evolution rewrites (aeon PRs #46–#136) introduced new exit taxonomies — `SKIP_UNCHANGED`, `NEW_INFO`, `SKIP_QUIET` — that classify quiet-but-correct runs separately from failures. Existing health checks treat any non-`*_OK` exit as worth attention; the analytics view makes the actual distribution visible so a skill running mostly `SKIP_UNCHANGED` reads as healthy-quiet, not silently broken.\n\n## Steps\n\n### 1. Determine the window\n\n- `WINDOW_HOURS` was set by the selector parse (default 168 = 7 days; a positive integer from `analytics:N`, `metrics N`, or a bare integer; capped at 720 = 30 days — anything longer slows the `gh api` paginate).\n- Compute `WINDOW_LABEL` (e.g. `\"last 7d\"` or `\"last 72h\"`).\n\n### 2. Pull the run snapshot\n\nWith WINDOW_HOURS written in literally. A shell `>` redirect into a file is refused in this run, so the JSON reaches its file through a piped one-line `node` writer:\n```bash\n.\u002Fscripts\u002Fskill-runs --json --hours \u003CWINDOW_HOURS> 2>\u002Fdev\u002Fnull | node -e 'require(\"fs\").writeFileSync(\"output\u002F.chains\u002Fskill-analytics-runs.json\", require(\"fs\").readFileSync(0))'\n```\n\nIf the script fails (auth, rate limit, network error) or the JSON is empty:\n- Log `SKILL_ANALYTICS_NO_DATA — skill-runs returned empty (gh api \u002F network error?)` to `memory\u002Flogs\u002F${today}.md` (under the `### skill-health` heading, see step 13) and stop with **no notification**. A silent fleet view is correct on data-fetch failure — fall back rather than guess.\n\nThe script's JSON shape (see `scripts\u002Fskill-runs`):\n```json\n{\n  \"period\": {\"since\": \"...\", \"until\": \"...\", \"hours\": 168},\n  \"summary\": {\"total\": N, \"succeeded\": N, \"failed\": N, \"cancelled\": N, \"in_progress\": N},\n  \"skills\": [{\"skill\": \"name\", \"total\": N, \"success\": N, \"failure\": N, \"cancelled\": N, \"in_progress\": N, \"last_run\": \"...\", \"last_conclusion\": \"...\"}],\n  \"anomalies\": {\"duplicates\": [...], \"failing\": [...]}\n}\n```\n\n### 3. Cross-reference with cron schedule\n\nRead `aeon.yml` and build `SCHEDULED_SKILLS`: dict `{skill_name -> {enabled: bool, schedule: str}}` for every entry under `skills:`. Treat `schedule: \"workflow_dispatch\"` and `schedule: \"reactive\"` as exempt from the \"no runs in window\" anomaly — those are dispatched on demand, not by cron.\n\nFor every skill in `SCHEDULED_SKILLS` where `enabled: true` AND schedule is a valid cron expression AND the skill is **not** present in the snapshot's `skills` array, mark `silent_scheduled: true` (zero runs in window despite an active schedule).\n\n### 4. Cross-reference with cron-state.json\n\nLoad `memory\u002Fcron-state.json` if present (missing → empty dict, not failure). For each skill in the snapshot, attach:\n- `consecutive_failures` (0 if missing)\n- `last_status` (`\"unknown\"` if missing)\n\nUsed to compute the consecutive-failure anomaly without a second `gh api` round-trip.\n\n### 5. Mine exit taxonomy from logs\n\nFor each daily log file `memory\u002Flogs\u002FYYYY-MM-DD.md` whose date falls in the window, scan for these markers (one match per skill section):\n- `_OK` → success (excluding `_OK_SILENT`)\n- `_OK_SILENT` \u002F `_QUIET` \u002F `SKIP_QUIET` → quiet-success\n- `SKIP_UNCHANGED` → skip-unchanged (autoresearch-evolution exit)\n- `NEW_INFO` → new-info (autoresearch-evolution exit)\n- `_SKIP*` (other) → skip-other\n- `_ERROR` \u002F `_FAILED` → error\n- `_PARTIAL` → partial\n- (no match) → uncategorized\n\nBuild `EXIT_DIST[skill]` = `{ok: N, quiet: N, skip_unchanged: N, new_info: N, skip_other: N, error: N, partial: N, uncategorized: N}`. The dominant bucket per skill is the one with the largest count; ties broken in the order listed above. If a skill has no log markers in the window, dominant bucket is `\"uncategorized\"`.\n\nThis step is best-effort — the markers are regex-grepped from human-written logs, not parsed from a contract. A miss-rate of 10–20% is expected and acceptable; the GitHub Actions success\u002Ffailure counts from step 2 remain the ground truth for pass\u002Ffail. The taxonomy distribution is a secondary signal.\n\n### 6. Anomaly classification\n\nFor each skill in the snapshot OR `silent_scheduled`, assign **at most one** anomaly flag, first match wins:\n\n| Flag | Trigger |\n|---|---|\n| `? SILENT` | `silent_scheduled: true` (enabled cron skill, zero runs in window) |\n| `? ALL_FAIL` | `total >= 2` AND `failure == total` |\n| `? CONSECUTIVE_FAILURES` | `consecutive_failures >= 3` (from cron-state) |\n| `? LOW_SUCCESS` | `total >= 3` AND `success \u002F total \u003C 0.80` |\n| `? ALL_SKIP` | `total >= 3` AND `EXIT_DIST.ok + EXIT_DIST.quiet + EXIT_DIST.new_info == 0` AND `EXIT_DIST.skip_unchanged + EXIT_DIST.skip_other > 0` (every run skipped — possibly correct, possibly stuck) |\n| `? DUPLICATE_RUNS` | `total > 2 × expected_runs(schedule, window)` (more runs than the cron should produce — manual reruns or scheduler glitch) |\n\n`expected_runs(schedule, window)` is a coarse estimate — for a cron `\"0 H * * *\"` over 7 days, expect 7; for `\"0 H,H,H * * *\"`, expect 21; for weekly `\"0 H * * D\"`, expect 1. If the schedule string is unparseable, skip the duplicate check for that skill (do not flag false positives).\n\nA skill with no flag is considered HEALTHY for analytics purposes.\n\n### 7. Compute summary\n\n```\ntotal_runs:          sum of every skill's total\ndistinct_skills:     count of skills with total >= 1\noverall_success_pct: snapshot.summary.succeeded \u002F (succeeded + failed) × 100  (cancelled + in_progress excluded)\nanomaly_count:       count of skills with any flag in step 6\nsilent_scheduled_count: count of SILENT flags\nexit_dominant:       top 3 dominant exit buckets across the fleet, e.g. \"ok (42), skip_unchanged (18), error (3)\"\n```\n\n### 8. Build the verdict line\n\nPick the strongest single claim, in priority:\n\n1. Any `? SILENT` exists → `\"${N} scheduled skill(s) didn't run this window — ${first_skill}\"`\n2. Any `? ALL_FAIL` exists → `\"${first_skill} failed every run (${N}\u002F${N}) — investigate\"`\n3. Any `? CONSECUTIVE_FAILURES` exists → `\"${first_skill} on ${N}-run failure streak\"`\n4. Any `? LOW_SUCCESS` exists → `\"${first_skill} ${pct}% success over ${total} runs — degraded\"`\n5. Any `? ALL_SKIP` exists → `\"${N} skill(s) only emitting skip-class exits this window — verify intent\"`\n6. Otherwise → `\"All ${distinct_skills} active skills healthy — ${overall_success_pct}% success across ${total_runs} runs\"`\n\n### 9. Significance gate\n\n**Notify only if `anomaly_count >= 1`.** Silent run = correct (no anomalies in fleet) = no notification. Following the autoresearch-evolution \u002F fork-digest pattern: noisy skills break trust faster than missing pings.\n\nIf gate says skip, still write the article and JSON spec, and log `SKILL_ANALYTICS_QUIET` (no anomalies). The dashboard widget refreshes regardless; only the push notification is gated.\n\n### 10. Write the article\n\nPath: `output\u002Farticles\u002Fskill-analytics-${today}.md`. Overwrite if it exists (idempotent same-day reruns).\n\n```markdown\n# Skill Analytics — ${today}\n\n**Verdict:** ${verdict_line}\n\n*Window: ${WINDOW_LABEL} · ${total_runs} runs across ${distinct_skills} skills · ${overall_success_pct}% success · ${anomaly_count} anomalies*\n\n## Anomalies\n\n| Flag | Skill | Detail | Action |\n|------|-------|--------|--------|\n| ? SILENT | name | scheduled `\u003Ccron>` but zero runs in window | run **aeon-doctor** — zero-runs-despite-schedule is usually a static config bug (unquoted `schedule:`, dup key) it can't be seen in run data |\n| ? ALL_FAIL | name | N\u002FN failed | investigate root cause |\n| ? CONSECUTIVE_FAILURES | name | N-run streak (last_error: \"...\") | see health view for filed issue |\n| ? LOW_SUCCESS | name | N% over M runs | review failures |\n| ? ALL_SKIP | name | M runs, all skip-class | confirm SKIP_UNCHANGED is the intent |\n| ? DUPLICATE_RUNS | name | M runs, expected ~K | check for manual reruns |\n\n(If `anomaly_count == 0`: write `No anomalies — fleet healthy across ${distinct_skills} skills.`)\n\n## Top runners (by run count)\n\n| # | Skill | Runs | Success | Last status | Dominant exit |\n|---|-------|------|---------|-------------|---------------|\n| 1 | name  | N    | XX%     | success     | ok            |\n| 2 | name  | N    | XX%     | success     | skip_unchanged |\n...\n\n(Top 15 by total runs desc. If fewer than 15 active skills, list all.)\n\n## Failure rate (sorted, ≥1 failure)\n\n| Skill | Runs | Failures | Success rate | Last conclusion |\n|-------|------|----------|--------------|-----------------|\n\n(All skills with `failure >= 1`, sorted by `failure \u002F total` desc. If none: \"Zero failures across ${distinct_skills} skills this window.\")\n\n## Exit taxonomy distribution\n\n| Bucket | Count | % | Top skills |\n|--------|-------|---|------------|\n| ok            | N | XX% | a, b, c |\n| skip_unchanged | N | XX% | d, e |\n| new_info      | N | XX% | f |\n| quiet         | N | XX% | g |\n| error         | N | XX% | h |\n| partial       | N | XX% |   |\n| uncategorized | N | XX% |   |\n\n(Sourced from `memory\u002Flogs\u002F*.md` — best-effort regex grep, see Step 5. Cell-aligns to summary cells above where available.)\n\n## Silent scheduled skills (enabled, zero runs)\n\n${list of {skill, schedule} pairs OR \"none — every enabled cron skill ran at least once.\"}\n\n> **Root-cause pointer:** an enabled cron skill with *zero* runs is most often a **static config** fault the run data can't reveal — an unquoted `schedule:` the scheduler regex silently skips, a duplicate `aeon.yml` key, or an entry pointing at a missing `SKILL.md`. That class is `aeon-doctor`'s job; dispatch it (`.\u002Faeon skills run aeon-doctor`) to pinpoint the cause instead of guessing at the scheduler.\n\n## Source status\n\n- skill-runs JSON: ${ok|empty|fetch_error}\n- Window: ${WINDOW_HOURS}h (${period.since} → ${period.until})\n- aeon.yml: ${ok|missing}\n- cron-state.json: ${ok|missing — first run for this fork?}\n- Daily logs scanned: ${N_LOG_FILES}\u002F${expected_log_files} for exit taxonomy\n\n---\n*The analytics view of `skill-health` (per-skill issue filing lives in the health view) and a companion to `heartbeat` (per-run pulse). Fleet-wide observability is the gap this view closes. Methodology: GitHub Actions run history is ground truth for pass\u002Ffail; daily-log markers are best-effort secondary signal for exit taxonomy.*\n```\n\n### 11. Write the dashboard JSON spec\n\nPath: `apps\u002Fdashboard\u002Foutputs\u002Fskill-analytics.json`. Use the catalog components (Card \u002F Stack \u002F Heading \u002F Text \u002F Badge \u002F Table).\n\n```json\n{\n  \"version\": \"1\",\n  \"generated_at\": \"${ISO timestamp}\",\n  \"skill\": \"skill-analytics\",\n  \"title\": \"Skill Analytics — ${today}\",\n  \"spec\": {\n    \"type\": \"Stack\",\n    \"props\": {\"direction\": \"vertical\", \"gap\": \"md\"},\n    \"children\": [\n      {\"type\": \"Heading\", \"props\": {\"level\": 2, \"children\": \"Skill Analytics — ${today}\"}},\n      {\"type\": \"Text\", \"props\": {\"variant\": \"muted\", \"children\": \"${verdict_line}\"}},\n      {\"type\": \"Grid\", \"props\": {\"columns\": 4, \"gap\": \"sm\"}, \"children\": [\n        {\"type\": \"Card\", \"props\": {\"children\": [\n          {\"type\": \"Text\", \"props\": {\"variant\": \"muted\", \"children\": \"Total runs\"}},\n          {\"type\": \"Heading\", \"props\": {\"level\": 3, \"children\": \"${total_runs}\"}}\n        ]}},\n        {\"type\": \"Card\", \"props\": {\"children\": [\n          {\"type\": \"Text\", \"props\": {\"variant\": \"muted\", \"children\": \"Active skills\"}},\n          {\"type\": \"Heading\", \"props\": {\"level\": 3, \"children\": \"${distinct_skills}\"}}\n        ]}},\n        {\"type\": \"Card\", \"props\": {\"children\": [\n          {\"type\": \"Text\", \"props\": {\"variant\": \"muted\", \"children\": \"Success rate\"}},\n          {\"type\": \"Heading\", \"props\": {\"level\": 3, \"children\": \"${overall_success_pct}%\"}}\n        ]}},\n        {\"type\": \"Card\", \"props\": {\"children\": [\n          {\"type\": \"Text\", \"props\": {\"variant\": \"muted\", \"children\": \"Anomalies\"}},\n          {\"type\": \"Heading\", \"props\": {\"level\": 3, \"children\": \"${anomaly_count}\"}}\n        ]}}\n      ]},\n      {\"type\": \"Heading\", \"props\": {\"level\": 3, \"children\": \"Top runners\"}},\n      {\"type\": \"Table\", \"props\": {\n        \"columns\": [\n          {\"key\": \"rank\", \"header\": \"#\"},\n          {\"key\": \"skill\", \"header\": \"Skill\"},\n          {\"key\": \"runs\", \"header\": \"Runs\"},\n          {\"key\": \"success\", \"header\": \"Success\"},\n          {\"key\": \"exit\", \"header\": \"Dominant exit\"}\n        ],\n        \"rows\": [\n          {\"rank\": \"1\", \"skill\": \"name\", \"runs\": \"N\", \"success\": \"XX%\", \"exit\": \"ok\"}\n        ]\n      }}\n    ]\n  }\n}\n```\n\nIf `anomaly_count >= 1`, prepend an `Alert` block before the verdict:\n\n```json\n{\"type\": \"Alert\", \"props\": {\"variant\": \"destructive\", \"children\": \"${anomaly_count} anomaly flag(s) raised — see Anomalies section\"}}\n```\n\nIf the file write fails (filesystem read-only, missing directory), log a warning but do not abort — the article is the canonical artifact, the JSON spec is a dashboard convenience.\n\n### 12. Send notification (only if gate from step 9 passed)\n\nVia `.\u002Fnotify`:\n\n```\n*Skill Analytics — ${today}*\n${verdict_line}\n\nWindow: ${WINDOW_LABEL} · ${total_runs} runs · ${distinct_skills} skills · ${overall_success_pct}% success\nAnomalies: ${anomaly_count}\n\n${If ? flags (cap top 3):}\n? Critical:\n- ${skill} — ${flag}: ${detail}\n\n${If ? flags (cap top 3):}\n? Degraded:\n- ${skill} — ${flag}: ${detail}\n\n${If ? flags (top 3, only if no ?\u002F? already filled the slots):}\n? Watch:\n- ${skill} — ${flag}: ${detail}\n\nTop by runs: ${top_3_skills_by_run_count_with_counts}\n\nFull: output\u002Farticles\u002Fskill-analytics-${today}.md\n```\n\nKeep the message body tight for signal. Drop the \"Top by runs\" line first if it runs long; flags are higher signal. (`.\u002Fnotify` auto-chunks, so length is about signal, not transport.)\n\n### 13. Log to `memory\u002Flogs\u002F${today}.md`\n\nLog under the shared `### skill-health` heading (the health loop parses this shape), with a `view: analytics` discriminator:\n\n```\n### skill-health\n- view: analytics\n- **Window**: ${WINDOW_LABEL} (${WINDOW_HOURS}h)\n- **Total runs**: ${total_runs} across ${distinct_skills} skills\n- **Overall success rate**: ${overall_success_pct}%\n- **Anomalies**: ${anomaly_count} (? ${red_count}, ? ${orange_count}, ? ${yellow_count})\n- **Silent scheduled**: ${silent_scheduled_count} skills (${comma list capped at 5})\n- **Top runner**: ${top_skill} (${top_runs} runs)\n- **Exit dominant**: ${exit_dominant_summary}\n- **Verdict**: ${verdict_line}\n- **Article**: output\u002Farticles\u002Fskill-analytics-${today}.md\n- **Dashboard**: apps\u002Fdashboard\u002Foutputs\u002Fskill-analytics.json\n- **Notification sent**: ${yes|no — quiet (no anomalies)}\n- **Status**: SKILL_ANALYTICS_OK | SKILL_ANALYTICS_QUIET | SKILL_ANALYTICS_NO_DATA\n```\n\n## Analytics-view exit taxonomy\n\n| Status | Meaning | Notify? |\n|--------|---------|---------|\n| `SKILL_ANALYTICS_OK` | snapshot fetched, ≥1 anomaly flagged | Yes |\n| `SKILL_ANALYTICS_QUIET` | snapshot fetched, zero anomalies | No (article + JSON written, log only) |\n| `SKILL_ANALYTICS_NO_DATA` | skill-runs returned empty \u002F fetch failed | No (log only, no article overwrite) |\n\n## Analytics-view constraints\n\n- **Significance-gated.** A clean fleet must produce zero notifications. Article and JSON spec still write so the dashboard reflects the latest state, but `.\u002Fnotify` is silent.\n- **Never invent runs.** If `skill-runs` returns empty, exit `SKILL_ANALYTICS_NO_DATA` — do not synthesise data from cron-state alone (cron-state's view is per-skill, not chronologically ordered, and would produce a misleading \"top runners\" table).\n- **Best-effort exit-taxonomy parsing.** Log markers are human-written; expect a 10–20% miss rate. Do not block the article on parse failures — drop the affected skill into `uncategorized` and continue.\n- **Idempotent.** Same-day reruns overwrite the article and JSON spec. The log entry is appended (one block per run, lets the operator see analytic drift across reruns).\n- **No issue filing in this view.** The analytics view does not write to `memory\u002Fissues\u002F` — that contract belongs to the health view. Anomalies surface here as flags; persistence and resolution live in the health view's domain.\n- **Respect workflow_dispatch \u002F reactive.** Skills with non-cron schedules cannot be SILENT — they fire only on demand. Excluding them from the silent-scheduled check prevents permanent false positives.\n\n---\n\n## Network note (both views)\n\nThis skill fetches no URLs directly — all data is local or via `gh` \u002F `.\u002Fscripts\u002Fskill-runs` (which uses `gh api`, so auth comes from `GITHUB_TOKEN` with no secret ever on the command line). No `curl` fallback needed.\n\n- **Health view:** if `.\u002Fscripts\u002Fskill-runs` fails, log `SKILL_HEALTH_PARTIAL — skill-runs unavailable` and continue with cron-state only.\n- **Analytics view:** if `gh api` is rate-limited or the runner's network is degraded, `.\u002Fscripts\u002Fskill-runs` exits non-zero; catch that and fall through to `SKILL_ANALYTICS_NO_DATA` rather than emitting a partial fleet view that would mislead.\n",{"name":5,"metadata":53,"scorable":49,"description":7},{"var":54,"tags":55,"title":57,"category":40},"",[56],"meta","Skill Health","main","b582c38fd13a043dca7ed24605354715d7a6e3f2","skills\u002Fskill-health",2,"https:\u002F\u002Fgithub.com\u002FMiroShark\u002Fmiroshark-aeon","https:\u002F\u002Ft.me\u002Fmiroshark_aeon","https:\u002F\u002Fgithub.com\u002FMiroShark","https:\u002F\u002Fwww.miroshark.xyz\u002F","Simulate anything, for $1 & less than 10 min.","2026-11-11 07:47:39",{"source":8,"skillName":5,"commands":69,"prompt":79,"zipAvailable":80},[70,73,76],{"tool":71,"command":72},"npx","npx skills add MiroShark\u002Fmiroshark-aeon --skill skill-health",{"tool":74,"command":75},"bunx","bunx skills add MiroShark\u002Fmiroshark-aeon --skill skill-health",{"tool":77,"command":78},"pnpm","pnpm dlx skills add MiroShark\u002Fmiroshark-aeon --skill skill-health","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FMiroShark\u002Fmiroshark-aeon\u002Fblob\u002Fmain\u002Fskills\u002Fskill-health\u002FSKILL.md",true,[],[83,116,139,165,190,213],{"skillNo":84,"name":85,"nameZh":6,"description":86,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":95,"publishedAt":48},"01M6J4P8XKB8RJ7PCYMZPZ0F6S","you-web-search","Web search using You.com Search API with high-quality, cited results — runs keyless out of the box; YDC_API_KEY unlocks higher limits and real-time web crawling","https:\u002F\u002Fgithub.com\u002FMiroShark\u002Fmiroshark-aeon\u002Fblob\u002Fmain\u002Fskills\u002Fyou-web-search\u002FSKILL.md",[89,92],{"categoryNo":90,"categoryName":91,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":93,"categoryName":94,"taxonomyType":28,"taxonomyTypeDesc":29},"01JBSEEDCAT000000000000030","开发工程师",[96,97,98,99,100,101,102,103,104,105,106,107,108,109,36,110,111,112,113,114,115],"web search","search","you","you web search","web","time","only","api","web crawling","read","read-only","real-time","basics","keyless","research","results","high","quality","box","limits",{"skillNo":117,"name":118,"nameZh":6,"description":119,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":120,"categories":121,"keywords":122,"publishedAt":48},"01M6J4P4H35WQG6RHGTTACSMVW","write-tweet","Multi-format tweet studio - standalone drafts (10 across 5 size tiers), a 5-10 tweet thread, or 10 remixes of past tweets, selected via ${var}","https:\u002F\u002Fgithub.com\u002FMiroShark\u002Fmiroshark-aeon\u002Fblob\u002Fmain\u002Fskills\u002Fwrite-tweet\u002FSKILL.md",[],[123,124,125,126,127,103,128,129,130,131,108,132,133,134,135,136,137,138],"write","write tweet","standalone","social","studio","across","xai","past","drafts","twitter","thread","multi","size","via","format","content",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":148,"publishedAt":48},"01M6J4P11K7ZJ2PJDHDTTFZ1HY","weekly-aeoncard","Build a weekly token-consumption recap image from memory\u002Ftoken-usage.csv - this week + all-time totals, top skills, rendered as a shareable SVG card.","https:\u002F\u002Fgithub.com\u002FMiroShark\u002Fmiroshark-aeon\u002Fblob\u002Fmain\u002Fskills\u002Fweekly-aeoncard\u002FSKILL.md",[145],{"categoryNo":146,"categoryName":147,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000016","图像与多媒体",[149,150,151,152,153,34,154,101,42,155,156,157,158,159,160,161,162,123,163,164],"weekly","weekly aeoncard","as","usage","SVG","card","image","recap","csv","monitoring","notifications","productivity","top","build","token","aeon",{"skillNo":166,"name":167,"nameZh":6,"description":168,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":169,"categories":170,"keywords":171,"publishedAt":48},"01M6J4NVJYQP5DNKSGXAXQW06B","vuln-tracker","One lifecycle poll over everything vuln-scanner produces - PR and advisory status, PVR triage transitions, and pending-disclosure aging, with a stars-secured impact headline and one action queue.","https:\u002F\u002Fgithub.com\u002FMiroShark\u002Fmiroshark-aeon\u002Fblob\u002Fmain\u002Fskills\u002Fvuln-tracker\u002FSKILL.md",[],[172,173,42,174,175,123,163,176,177,178,179,180,181,182,183,184,185,36,186,187,188,189],"tracker","vuln tracker","impact","scanner","status","action","security","pr","stars","lifecycle","dev","gh","aging","advisory","github","transitions","triage","global",{"skillNo":191,"name":192,"nameZh":6,"description":193,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":194,"categories":195,"keywords":199,"publishedAt":48},"01M6J4NNQCHTKNGENAPJQ8KYHR","vuln-scanner","Audit trending repos for real security vulnerabilities and disclose responsibly - scan and route findings (PVR \u002F dependency PR), re-submit queued advisories, and send armed email disclosures","https:\u002F\u002Fgithub.com\u002FMiroShark\u002Fmiroshark-aeon\u002Fblob\u002Fmain\u002Fskills\u002Fvuln-scanner\u002FSKILL.md",[196],{"categoryNo":197,"categoryName":198,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000005","邮件与沟通",[175,200,201,202,203,42,204,178,205,206,207,182,186,208,189,209,103,210,211,212],"vuln scanner","resend","re","trending","repos","email","for","scan","vulnerabilities","submit","route","audit","send",{"skillNo":214,"name":215,"nameZh":6,"description":216,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":217,"categories":218,"keywords":220,"publishedAt":48},"01M6J4NFBXTGG0R4M484T0KNYW","video-script","Turn a repo, product page, or update into a recording-ready video script in a receipts-first format - verifies every claim against live sources, then writes timestamped VO + on-screen direction with a plain-language glossary, assets checklist, anti-tells, and a verify-before-recording list","https:\u002F\u002Fgithub.com\u002FMiroShark\u002Fmiroshark-aeon\u002Fblob\u002Fmain\u002Fskills\u002Fvideo-script\u002FSKILL.md",[219],{"categoryNo":146,"categoryName":147,"taxonomyType":20,"taxonomyTypeDesc":21},[221,222,223,224,225,226,227,228,229,108,230,231,232,36,233,234,235,236,237,137,238,239,240],"Video","video script","script","recording","sources","ready","direction","page","repo","claim","plain-language","glossary","first","verify","product","screen","update","list","live","receipts"]