graph-audit — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited graph-audit (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Graph audit has three evidence planes: authored source (SKILL.md plus audit-state.json), compiled projection (manifest samples and exports), and executable or report gates (lint, drift, protocol consistency, schema constants, routing eval, application-eval shape, audit-manifest, conformance ledger, inline-count guard, Markdown links, and preflight).
SKILL.md metadata validates against schemas/SKILL_METADATA_PROTOCOL_schema.json: required v8 identity fields, subject/public enums, name shape, and name-directory parity.audit-state.json validates against schemas/skill-audit-state.schema.json, and audit/eval/provenance fields live in the sidecar instead of being duplicated as hand-authored SKILL.md state.SKILL.md (the 25 agent-facing frontmatter fields) plus its sibling audit-state.json (the audit/eval/provenance fields), governed by two distinct schemas. scripts/check-schema-constants.js asserts BOTH required sets (the 5 core frontmatter fields and the 7 required sidecar fields) and reads schema_version + the four verdicts from the sidecar — a field placed in the wrong file is a real finding even when each file independently parses.examples/skills.manifest.sample.json and generated manifests are the joined model of SKILL.md + audit-state.json, not a stale copy of either file alone (node scripts/generate-manifest.js --validate-only).check-protocol-consistency.js, check-schema-constants.js).relations.* corresponds to a real sibling skill directory and uses the right predicate semantics (related/broader/narrower/boundary/disjoint_with/verify_with/depends_on). *No deterministic gate currently enforces target existence. `scripts/skill-lint.js` deliberately does NOT walk relation targets (that check was removed in the 2026-05-19 lint reduction — it now only emits advisory boundary warnings, checks 5b/5c), and `scripts/generate-manifest.js` copies the `relations` block through unmodified (`generate-manifest.js:360-382`); its `--validate-only` pass validates the compiled manifest against `schemas/manifest.schema.json`, which checks each relation item's shape, not whether its target resolves to a real skill. So a dangling edge is surfaced only by the audit pipeline's graded Relation quality surface (`lib/audit/skill-audit.js:550`) and the human-facing fix-hint at `lib/audit/skill-audit.js:413` that recognizes a "does not match any known skill" message — both semantic/graded, not a CI gate. An auditor must therefore resolve relation targets by hand (glob the sibling library) rather than trusting `manifest:validate` to do it.* This live ownership gap also exposes stale docs (see § Known Stale Claims).scripts/skill-lint.js emits advisory warnings (checks 5b/5c) when a relations.suppresses reason reads as deference ("use X instead") rather than ownership ("I own this over X"), or when a suppresses edge crosses subject shelves (a cross-domain distinction belongs in anti_examples + relations.related, not an exclusion guard).drift_check.truth_source_hashes still match the live truth-source files via scripts/skill-graph-drift.js; this is the mechanism behind truth_verdict. A truth source that changed since last_verified without a re-recorded hash is silent staleness, and a live drift result is stronger evidence than a stale sidecar truth_verdict: PASS.routing_eval: present is backed by a current routing-harness run (scripts/skill-graph-routing-eval.js) over a manifest generated from current source, with positive examples routing to the skill and anti-examples blocked or explained.eval_artifacts: present and graded verdict claims are backed by real gradeable artifacts. scripts/check-audit-manifest.js enforces that a graded comprehension_verdict (PROVISIONAL/PASS/SHALLOW/REDUNDANT) requires evals/comprehension.json on disk, and a high-stakes graded application_verdict (APPLICABLE/MIXED/HARMFUL) requires evals/application.json.scripts/check-application-evals.js validates each evals/application.json against schemas/application.schema.json: the mode: "application" discriminator, the ≥5-case floor, the per-case required-field set, unique ids, and the red-herring recommendation.comprehension_state: present, it must carry the five flat top-level Understanding fields (mental_model, purpose, boundary, analogy, misconception); the legacy nested concept block is accepted for back-compat only, and the content must contain no out-of-scope repo-specific nouns. This is audit checklist item 8; scripts/skill-audit-preflight.js reports whether the fields an op needs (including these) are present before audit/improve/evaluate runs.scripts/skill-conformance-ledger.js turns the binary lint gate into a per-shelf "X / N clean" number plus an error-type histogram — the incremental signal the all-or-nothing gate cannot give while the corpus is draining through the audit loop.docs/status.generated.md + the live find command, never hand-stamped into prose; scripts/check-inline-skill-counts.js (npm run counts:check) fails the build on any inline count.scripts/check-markdown-links.js catches dangling intra-repo links (paths and anchors) emitted by skill bodies and docs — a broken [...](path) is the same silent-failure class as a dangling relation.project[] skill has a populated grounding declaration with subject_matter, grounding_mode, truth_sources, failure_modes, and evidence_priority. The schema's allOf requires only grounding for a project target; project[] is an optional belonging-entity affiliation, not a requirement.name field matches the name of its parent directory (required for SKILL.md compatibility).Skill graphs fail silently. A broken relation, stale truth-source hash, or drifted enum value does not crash the agent — it changes what the router loads, what the manifest exports, or what the auditor trusts. That kind of failure is worse than a crash because the system still looks healthy. The audit's job is to turn every silent bug into a loud one before the graph accumulates enough drift that agents can no longer trust its edges.
A graph audit is a constellation of single-purpose checkers, not one mega-linter. This is a deliberate design choice: scripts/skill-lint.js was reduced (2026-05-19) to enforce only external-mandate, canonical-source constraints — valid YAML, schema validation, identifier shape, non-empty description, name/directory parity, and subjects[0]/subject agreement (plus advisory boundary-orientation warnings). Everything else — manifest parity, eval coherence, drift, routing — lives in dedicated tools (the drift sentinel, the manifest validator, the routing engine, the audit-manifest checker, the application-eval checker, the conformance ledger). Auditing well means knowing which checker owns which invariant and running the right one, not assuming lint covers everything. The sharpest example of this trap is relation-target existence: it is owned by no deterministic checker today (see Coverage → Relation integrity), so an auditor who assumes a green lint/manifest:validate proves every edge resolves will ship a dangling edge.
Two integrity surfaces sit side by side. The binary gate (npm run lint / npm run verify) goes red if any skill fails, which is the correct ship-blocker but gives no incremental signal mid-migration. The conformance ledger (skill-conformance-ledger.js) is report-only and turns the same per-skill linter into a measurable percentage so progress is visible while the corpus drains. Use the gate to block a release; use the ledger to watch the backlog move.
The audit's reason to exist is upstream-confirmed: the base Agent Skills spec is still intentionally minimal — only name and description are required, with license, compatibility, metadata, and allowed-tools optional, and metadata treated as arbitrary additional data that downstream runtimes (e.g. OpenCode) ignore when unknown. No upstream release has absorbed the Skill Metadata Protocol's typed scope, relations, grounding, or eval state, so every invariant this skill audits is a Protocol addition layered on the minimal base — which is exactly why a dedicated audit discipline is needed: the upstream spec will never check these for you.
Treat the library like a small database system across three evidence planes:
SKILL.md is the authored capability row (routing contract, scope, relations, grounding, agent-facing guidance); audit-state.json is the sidecar state row (owner, freshness, drift hashes, eval state, routing coverage, the four verdicts).related = adjacency, boundary = score-aware exclusion when this skill wins, depends_on/broader expand loading, verify_with = cross-check). The audit commands are integrity checks against those planes — but some questions are intentionally runbook-owned rather than standalone-gate-owned.Do not close a finding on the wrong plane. A linter pass does not prove truth-source freshness. A sample-manifest routing pass does not prove the canonical source still has the same relations. A sidecar PASS verdict does not outweigh a fresh drift command showing changed truth sources.
| File | Line range | Purpose |
|---|---|---|
schemas/SKILL_METADATA_PROTOCOL_schema.json | whole file | Authored metadata schema for every SKILL.md (v8 classification, relations, grounding, project affiliation, Understanding fields) |
schemas/skill-audit-state.schema.json | whole file | Sidecar schema for freshness, drift hashes, eval state, routing coverage, and the four audit verdicts |
schemas/manifest.schema.json | whole file | Compiled-manifest shape and summary rollup — relation-item shape only, NOT relation-target existence |
schemas/application.schema.json | whole file | Binding shape for evals/application.json (mode discriminator, ≥5-case floor, per-case fields, ids, red-herring) |
skill-metadata-protocol/SKILL_METADATA_PROTOCOL.md | Schema contract + field sections | Source of truth for field semantics and authoring rules (note: § Relations overstates relation-target validation — a stale claim to flag; see § Known Stale Claims) |
skill-audit-loop/SKILL_AUDIT_LOOP.md | Part 2 + Part 3 | Binding audit runbook for per-skill checks that are not fully standalone-gate-owned, including body-section expectations |
docs/manifest-field-mapping.md | whole file | Authored-to-generated projection rules — trace a parity finding back to its projection |
SKILL_GRAPH.md | § Current State | Single source of truth for live corpus facts (schema version, required axes, counts); audits cite it rather than restating numbers |
scripts/skill-lint.js | whole file | Per-skill structural gate — external-mandate-only (valid YAML, schema validation, name shape, non-empty description, name/directory parity, subjects[0]/subject agreement) plus advisory boundary-orientation warnings (5b/5c at :419/:467). Does NOT check relation-target existence, manifest parity, or eval coherence |
lib/audit/skill-audit.js | :413 (fix-hint), :550 (Relation quality) | The graded Relation quality surface (:550) is the only place a dangling target is surfaced; :413 supplies the human-facing remediation hint on a "does not match any known skill" message. Neither is a deterministic CI gate |
scripts/skill-graph-drift.js | whole file | Drift sentinel — compares recorded drift_check.truth_source_hashes against live truth-source files; feeds truth_verdict; can record or stamp drift status |
scripts/generate-manifest.js | :360-382 (relations copy-through), :845-857 (validate) | Builds the joined manifest from SKILL.md + audit-state.json; --validate-only is the manifest-parity gate. It copies relations through unmodified and validates only manifest shape — it does NOT verify relation targets resolve to real skills |
scripts/check-protocol-consistency.js | whole file | Cross-artifact protocol checker; validates that protocol docs/mapping/sample stay consistent with schemas |
scripts/check-schema-constants.js | whole file | Fast guard for hard schema constants across BOTH frontmatter and sidecar schemas (required fields, enums, manifest root version, verdict fields) — the ADR-0019 two-file enforcer |
scripts/check-audit-manifest.js | whole file | Checks that graded verdict claims have the gradeable artifacts they require (comprehension/application evidence-honesty gate) |
scripts/check-application-evals.js | whole file | Structural conformance of every evals/application.json against the application schema (floor, fields, ids, red-herring) |
scripts/check-markdown-links.js | whole file | Dangling intra-repo markdown-link/anchor checker for skill bodies and docs |
scripts/check-inline-skill-counts.js | whole file | Guard that prevents volatile corpus counts from being hand-stamped into active prose |
scripts/skill-graph-routing-eval.js | whole file | Routing-coverage check against the retrieval baseline (evals/retrieval-baseline-*.json); the honest source for flipping routing_eval: absent → present. See also docs/ROUTING-METRICS.md |
scripts/skill-audit-preflight.js | whole file | Readiness gate — does a skill carry the version/fields/evals (incl. the five Understanding fields) an op needs before audit/improve/evaluate (exit 0 ready / 2 gaps; --ensure scaffolds deterministic gaps) |
scripts/skill-conformance-ledger.js | whole file | Report-only per-shelf "X / N clean" ledger + error-type histogram for tracking corpus health between gate runs |
docs/status.generated.md | whole file | Generated corpus-status source; link to it instead of restating live counts in prose |
docs/conformance.generated.md | whole file | Generated conformance-ledger report; useful for shelf-level cleanup triage |
docs/QUICKSTART-30MIN.md | § "Break the relation" | Current doc surface with a stale relation-lint claim — treat as a concrete audit finding until corrected (see § Known Stale Claims) |
examples/skills.manifest.sample.json | whole file | Generator-produced sample fixture; trustworthy only when fresh against the current canonical source + sidecar state |
examples/evals/graph-audit.json | whole file | Repo-grounded fixture for graph-audit prompts and expected evidence |
skill-lint.js is the first smoke test, not the whole audit. A skill that lints clean can still have a dangling relation, a drifted truth source, a stale manifest, or a dishonest verdict — those are caught by the other checkers, and a dangling relation target is caught by NO deterministic checker, so resolve those by hand. Use the smallest command that owns the question.
# Structural SKILL.md + sidecar checks for one skill
node scripts/skill-lint.js graph-audit --no-color
node scripts/skill-lint.js --path ../skills/skills graph-audit # explicit root for the sibling library
node scripts/skill-lint.js --include-template # also lint the example template
# Truth-source freshness for one skill directory
node scripts/skill-graph-drift.js ../skills/skills/quality-assurance/graph-audit
# Manifest projection and schema validation (NOT relation-target existence)
node scripts/generate-manifest.js --validate-only
# Protocol constants and schema/docs/mapping parity
node scripts/check-schema-constants.js
node scripts/check-protocol-consistency.js
# Routing-eval honesty for this skill against a fresh manifest fixture
node scripts/skill-graph-routing-eval.js --skill graph-audit --manifest examples/skills.manifest.sample.json --confusion-matrix
# Audit/eval evidence consistency + application-eval shape
node scripts/check-audit-manifest.js
node scripts/check-application-evals.js --skill graph-audit --check
# Corpus conformance triage + volatile-count hygiene + Markdown links
node scripts/skill-conformance-ledger.js --json
node scripts/check-inline-skill-counts.js
node scripts/check-markdown-links.js
# Audit-loop readiness before comprehension/application work
node scripts/skill-audit-preflight.js graph-audit --for allInterpretation rules:
skill-lint.js means structural checks passed. It does NOT prove drift freshness, manifest sync, routing behavior, or audit-artifact readiness.skill-graph-drift.js means at least one local truth source changed or broke relative to the sidecar hash baseline. Treat this as current truth evidence even if the sidecar still says truth_verdict: PASS.generate-manifest.js --validate-only can fail because of unrelated corpus skills. Separate per-skill graph-audit findings from corpus-wide migration debt — and remember it validates manifest shape, never relation-target existence.check-audit-manifest.js proves graded verdict claims have artifacts. It does not certify usefulness; application_verdict: APPLICABLE is the behavior-certification signal.check-application-evals.js --check proves application-eval JSON is structurally conformant. It does not prove the grader has run or that the application verdict is useful.skill-conformance-ledger.js is the incremental corpus-drain signal: per-shelf clean-count + error-type histogram help prioritize cleanup; the plain report is not a binary gate unless a threshold flag is used.check-inline-skill-counts.js proves active docs are not hand-stamping volatile corpus totals — generated status/conformance docs own the live numbers.check-markdown-links.js proves local Markdown targets and anchors resolve. It does not decide whether a skill's H2/H3 body structure is pedagogically complete; use the audit-loop runbook for that.Choose the umbrella gate by work mode:
npm run verify # full gate — corpus lint + manifest validation + routing eval + export (CONTENT-inclusive)
npm run verify:system # SYSTEM-only gate — schema constants, protocol, docs, fixtures, marketplace, unit tests
npm run release:check # the release-critical corpus gates verify:system OMITS (manifest:validate, routing-eval, export, marketplace:verify)verify:system green does not mean publishable: it deliberately omits the corpus gates, so it can stay green while manifest:validate / routing-eval are red (e.g. skills missing a required scope). Before claiming the library is publishable, run npm run release:check.
Because no deterministic gate owns relation-target existence, audit relations by hand:
relations from the canonical SKILL.md, then compare against the generated manifest entry to catch stale projection.related is browse/routing adjacency; boundary excludes listed skills from co-routing when this skill wins; verify_with names cross-check context; depends_on/broader expand loading; disjoint_with is mutual exclusion.relations.suppresses entries for ownership wording — a good reason says "graph-audit owns metadata-integrity verification over X"; a weak reason says "use X instead."skill-lint.js proves relation targets exist (the live lint only owns relation shape via schema validation plus boundary reason/cross-domain warnings), and do NOT claim generate-manifest.js --validate-only proves target existence (it copies relation fields through and schema-validates shape). If no deterministic command owns the target-existence check, report that as a tooling gap rather than claiming a gate already proved it.Two repo docs currently assert relation-target validation that the live code does not perform — both are real findings a graph audit should raise (schema/code wins over prose per the Doc Ownership Map precedence):
docs/QUICKSTART-30MIN.md (§ "Break the relation and watch lint catch it") states the lint "walks every relation predicate … and verifies that every named target resolves to a real sibling skill." False against current scripts/skill-lint.js (relation-target checking was removed in the 2026-05-19 lint reduction; only advisory boundary checks 5b/5c remain).skill-metadata-protocol/SKILL_METADATA_PROTOCOL.md § Relations states "the manifest compiler refuses to emit a relation to a skill that does not exist" and to "rely on npm run manifest:validate for the relation-integrity guarantee." False against current scripts/generate-manifest.js, which copies relations through unmodified (:360-382) and validates only manifest shape (:845-857).Record both as documentation drift until a deterministic relation-target checker is added or the docs are corrected. The general lesson generalizes: a sidecar truth_verdict: PASS recorded on an earlier date is an audit record, not live truth — re-run skill-graph-drift.js and trust the live result when they disagree.
This skill has a local comprehension artifact at evals/comprehension.json and a Skill Graph fixture at examples/evals/graph-audit.json. The local artifact exercises the portable concept (sidecar ownership, drift versus lint, manifest joins); the Skill Graph fixture keeps repo-grounded prompts tied to current protocol files, sidecar state, manifest joins, routing examples, relation metadata, and gradeable-artifact evidence.
No evals/application.json exists yet. The sidecar's eval_artifacts: present (ADR-0019 places eval_artifacts in audit-state.json, not frontmatter) is disk-truth meaning an eval file exists on disk — here the comprehension artifact — NOT a claim that an application eval exists; both comprehension_verdict and application_verdict stay honestly UNVERIFIED. The moment a graded application_verdict is claimed, check-audit-manifest.js will require an evals/application.json on disk — so do not stamp a graded application verdict without authoring (and running) that artifact in the same change. Before claiming comprehension/application readiness, run preflight: comprehension readiness requires the sidecar state plus the five flat Understanding fields and a sufficiently sized comprehension eval; application readiness requires a gradeable application artifact that passes the application-eval shape gate.
SKILL.md passes schema validation and name-directory parity.audit-state.json validates and owns audit/eval/provenance state rather than duplicating those fields in SKILL.md.SKILL.md + audit-state.json source model (generate-manifest.js --validate-only).check-protocol-consistency.js, check-schema-constants.js).skill-graph-drift.js).skill-graph-routing-eval.js).check-audit-manifest.js).evals/application.json meets the schema floor (check-application-evals.js) when application readiness is claimed.comprehension_state: present carries the five flat Understanding fields with no out-of-scope repo-specific nouns.project[] skills have a complete grounding declaration (and project affiliation where the protocol requires one).check-markdown-links.js); body H2/H3 structure findings are tied to the audit-loop runbook, not overstated as schema failures.check-inline-skill-counts.js); skill-conformance-ledger.js consulted when the question is corpus-wide cleanup priority.docs/QUICKSTART-30MIN.md and SKILL_METADATA_PROTOCOL.md § Relations do not continue to claim lint/manifest walks all relation targets — or that stale claim is recorded as doc drift.name field matches its parent directory name (SKILL.md compatibility).After applying this skill, verify:
## Do NOT Use When do not point to a better skill.| Use instead | When |
|---|---|
writing-humanizer | The task is making skill prose read more naturally, not auditing metadata or graph integrity |
refactor | The task is restructuring skill body sections while keeping the audit contract stable |
debugging | The task is chasing a runtime failure in an agent or application, not validating graph metadata |
skill-infrastructure | The task is designing, implementing, or maintaining the health-check tooling itself |
skill-router | The task is diagnosing why the router does or doesn't activate a skill on a query — a routing-mechanism question, not a metadata-consistency one |
schema-evolution | The task is changing the schema contract (bumping schema_version, migrating fields) rather than checking conformance against the current one |
testing-strategy | The task is choosing which tests to write for new code, rather than running or interpreting Skill Graph audit gates |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.