Config Validator
Config Validator checks `aeon.yml` and `.github/workflows/aeon.yml` for structural invariants that have caused production outages: missing or conditionally-gated checkout steps that disable all skills in a run, and duplicate YAML keys that silently shadow skill configurations. Use this skill weekly as a safety net to detect configuration drift on the main branch outside of pull request validation.
git clone --depth 1 https://github.com/aaronjmars/aeon /tmp/config-validator && cp -r /tmp/config-validator/skills/config-validator ~/.claude/skills/config-validatorSKILL.md
Today is ${today}. Your task is to validate the structural correctness of `aeon.yml` and `.github/workflows/aeon.yml`.
This skill exists because two incident classes have caused major outages:
- **Checkout-ordering class**: `Early checkout` step missing or conditionally gated — can cause every skill to fail in a single run.
- **Duplicate-key class**: duplicate YAML keys in `aeon.yml` — silently disable any skill whose key is shadowed.
The same checks also run as a pre-merge CI workflow at `.github/workflows/ci-config-validate.yml` (via `scripts/validate-config.js`) — that workflow blocks PRs that break these invariants. This skill is the **weekly safety net** for state that drifts on `main` outside of PRs (manual edits, scheduled rewrites, post-process commits). Run all checks. Report findings. Alert if any fail.
### Fast path — invoke the shared validator
The fastest, most consistent way to run all three checks is the shared script the CI workflow uses:
```bash
node scripts/validate-config.js
```
Exit code 0 = CLEAN (no notification needed). Non-zero exit + `FAIL[*]:` lines on stdout = ISSUES (skip to step 4).
If the script is unavailable for any reason, fall back to the manual checks in steps 1–3.
## Steps
### 1. Check workflow step ordering (checkout-ordering class)
Read `.github/workflows/aeon.yml`.
Find the `jobs.run.steps` array. Verify:
a. A step named `Early checkout` (or `uses: actions/checkout`) exists.
b. That step has **no** `if:` condition — it must run unconditionally.
c. That step is positioned **before** any step that has an `if:` condition.
Use node inline to parse and check:
```bash
node -e "
const fs = require('fs');
const text = fs.readFileSync('.github/workflows/aeon.yml', 'utf8');
const lines = text.split('\n');
let inSteps = false, stepDepth = 0;
let steps = [], cur = null, lineNum = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmed = line.trim();
if (/^\s{4,6}steps:/.test(line)) { inSteps = true; continue; }
if (!inSteps) continue;
if (/^\s{4,6}[a-z]/.test(line) && !/^\s{6,}/.test(line) && i > 0) { inSteps = false; continue; }
if (/^\s{6}- /.test(line)) {
if (cur) steps.push(cur);
cur = { lineNum: i + 1, name: null, hasIf: false, isCheckout: false };
}
if (cur) {
if (/name:/.test(trimmed)) cur.name = trimmed.replace(/^name:\s*/, '').replace(/[\"']/g, '');
if (/uses:\s*actions\/checkout/.test(trimmed)) cur.isCheckout = true;
if (/Early checkout/.test(trimmed)) cur.isCheckout = true;
if (/^\s{6}if:/.test(line)) cur.hasIf = true;
}
}
if (cur) steps.push(cur);
let issues = [];
const checkoutIdx = steps.findIndex(s => s.isCheckout);
if (checkoutIdx === -1) {
issues.push('FAIL: No checkout step (actions/checkout or Early checkout) found in jobs.run.steps');
} else {
const cs = steps[checkoutIdx];
if (cs.hasIf) {
issues.push('FAIL: Checkout step at line ' + cs.lineNum + ' has an if: condition — must be unconditional');
}
const firstConditional = steps.findIndex(s => s.hasIf);
if (firstConditional !== -1 && firstConditional < checkoutIdx) {
issues.push('FAIL: Checkout step appears after a conditional step at line ' + steps[firstConditional].lineNum);
}
if (issues.length === 0) {
console.log('PASS checkout: Early checkout is unconditional and first (line ' + cs.lineNum + ')');
}
}
if (issues.length > 0) { issues.forEach(i => console.log(i)); process.exit(1); }
"
```
If the check fails, record the finding. Continue to next check regardless.
---
### 2. Check for duplicate skill keys (duplicate-key class)
Read `aeon.yml`. Scan the `skills:` block for duplicate top-level keys.
```bash
node -e "
const fs = require('fs');
const text = fs.readFileSync('aeon.yml', 'utf8');
const lines = text.split('\n');
let inSkills = false;
const seen = {};
const dupes = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (/^skills:/.test(line)) { inSkills = true; continue; }
if (inSkills && /^[a-z]/.test(line)) { inSkills = false; continue; }
if (!inSkills) continue;
const match = line.match(/^ ([a-z][a-z0-9-]+):/);
if (match) {
const key = match[1];
if (seen[key]) {
dupes.push('FAIL: Duplicate skill key \"' + key + '\" at line ' + (i+1) + ' (first seen line ' + seen[key] + ')');
} else {
seen[key] = i + 1;
}
}
}
if (dupes.length > 0) { dupes.forEach(d => console.log(d)); process.exit(1); }
else { console.log('PASS duplicates: no duplicate skill keys found (' + Object.keys(seen).length + ' skills)'); }
"
```
If duplicates are found, record them. Continue to next check regardless.
---
### 3. Check all enabled skills have SKILL.md (missing-file class)
Read `aeon.yml`. For every skill with `enabled: true`, verify `skills/<name>/SKILL.md` exists.
```bash
node -e "
const fs = require('fs');
const text = fs.readFileSync('aeon.yml', 'utf8');
const lines = text.split('\n');
let inSkills = false;
const issues = [];
const ok = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (/^skills:/.test(line)) { inSkills = true; continue; }
if (inSkills && /^[a-z]/.test(line)) { inSkills = false; continue; }
if (!inSkills) continue;
const match = line.match(/^ ([a-z][a-z0-9-]+):\s*\{(.+)\}/);
if (match) {
const name = match[1];
const props = match[2];
if (/enabled:\s*true/.test(props)) {
const skillFile = 'skills/' + name + '/SKILL.md';
if (!fs.existsSync(skillFile)) {
issues.push('WARN: enabled skill \"' + name + '\" has no SKILL.md at ' + skillFile);
} else {
ok.push(name);
}
}
}
}
if (issues.length > 0) { issues.forEach(i => console.log(i)); }
console.log('PASS skill-files: ' + ok.length + ' enabled skills have SKILL.md' + (issues.length > 0 ? ', ' + issues.length + ' missing' : ''));
if (issues.length > 0) process.exit(1);
"
```
---
### 4. Summarize findings
After running all three checks, collect results:
- Count PASMention/keyword sweep on social platforms for [REPLACE: KEYWORDS] — trends, sentiment, top posts
5 concrete real-life actions, leverage-scored against open loops with specificity and anti-fluff gates
Curated AI-agent tweets, clustered into narratives with insight summaries
Tracker of AI agent substitution signals — which roles, companies, and industries show real headcount displacement. Named roles + real deployments only.
Competitive-intelligence digest on the AI agent framework space — momentum, releases, breaking changes across a curated watchlist
Cross-domain market pulse from AIXBT's free grounding endpoint — crypto, macro, tradfi, geopolitics. Refreshes taxonomy references (clusters, chains) as a bonus.
Pre-batch API provider health check — detects credit exhaustion or auth failure for every configured provider key before the scheduled batch runs, giving the operator a window to act before skills degrade
List a wallet's live ERC-20 token approvals on Base and flag unlimited / risky spender grants. Keyless via Base RPC (eth_getLogs + eth_call) — no explorer key needed.