@skill-graph/cli 0.5.6 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CHANGELOG.md +27 -3
  2. package/README.md +103 -31
  3. package/SKILL_GRAPH.md +2 -2
  4. package/bin/skill-graph.js +150 -10
  5. package/docs/ADOPTION.md +1 -1
  6. package/docs/PRIMER.md +6 -5
  7. package/docs/QUICKSTART-30MIN.md +2 -2
  8. package/docs/SKILL_AUDIT_CHECKLIST.md +1 -1
  9. package/docs/SKILL_METADATA_PROTOCOL.md +2 -2
  10. package/docs/_archived/marketplace-publication-priority-2026-05-18.md +1 -1
  11. package/docs/_drafts/0.5.8-release-prep.md +164 -0
  12. package/docs/adr/0009-sibling-repo-deprecation.md +48 -0
  13. package/docs/diagrams/skill-graph-ecosystem.mmd +17 -0
  14. package/docs/field-reference.generated.md +22 -2
  15. package/docs/field-reference.md +53 -2
  16. package/docs/images/skill-graph-ecosystem.svg +1 -0
  17. package/docs/manifest-field-mapping.md +3 -3
  18. package/docs/marketplace-publication-queue.generated.md +2 -2
  19. package/docs/plans/scripts-roadmap.md +2 -2
  20. package/docs/positioning.md +88 -0
  21. package/docs/research/skill-comprehension-eval-research.md +5 -5
  22. package/docs/research/skill-demand-gap-roadmap-2026-05-16.md +215 -0
  23. package/docs/status.generated.md +48 -0
  24. package/examples/audits/context-graph/findings.md +59 -0
  25. package/examples/audits/context-graph/scorecard.md +22 -0
  26. package/examples/audits/context-graph/verdict.md +33 -0
  27. package/examples/evals/a11y.json +45 -13
  28. package/examples/evals/api-design.json +18 -5
  29. package/examples/evals/code-review.json +18 -5
  30. package/examples/evals/data-modeling.json +18 -5
  31. package/examples/evals/database-migration.json +18 -5
  32. package/examples/evals/debugging.json +37 -11
  33. package/examples/evals/dependency-architecture.json +18 -5
  34. package/examples/evals/design-system-architecture.json +18 -5
  35. package/examples/evals/error-tracking.json +18 -5
  36. package/examples/evals/event-contract-design.json +18 -5
  37. package/examples/evals/form-ux-architecture.json +18 -5
  38. package/examples/evals/framework-fit-analysis.json +18 -5
  39. package/examples/evals/graph-audit.json +55 -13
  40. package/examples/evals/information-architecture.json +18 -5
  41. package/examples/evals/interaction-feedback.json +18 -5
  42. package/examples/evals/interaction-patterns.json +18 -5
  43. package/examples/evals/layout-composition.json +18 -5
  44. package/examples/evals/lint-overlay.json +38 -11
  45. package/examples/evals/microcopy.json +18 -5
  46. package/examples/evals/observability-modeling.json +18 -5
  47. package/examples/evals/pattern-recognition.json +32 -9
  48. package/examples/evals/performance-engineering.json +18 -5
  49. package/examples/evals/refactor.json +41 -12
  50. package/examples/evals/semiotics.json +18 -5
  51. package/examples/evals/skill-infrastructure.json +32 -9
  52. package/examples/evals/skill-router.json +42 -13
  53. package/examples/evals/system-interface-contracts.json +18 -5
  54. package/examples/evals/task-analysis.json +18 -5
  55. package/examples/evals/testing-strategy.json +36 -11
  56. package/examples/evals/type-safety.json +251 -66
  57. package/examples/evals/visual-design-foundations.json +18 -5
  58. package/examples/evals/webhook-integration.json +18 -5
  59. package/examples/fixture-skills/README.md +47 -0
  60. package/examples/fixture-skills/comprehension-full/SKILL.md +79 -0
  61. package/examples/fixture-skills/minimal-capability/SKILL.md +51 -0
  62. package/examples/fixture-skills/with-grounding/SKILL.md +78 -0
  63. package/examples/fixture-skills/with-relations/SKILL.md +87 -0
  64. package/examples/skills.manifest.sample.json +1722 -446
  65. package/marketplace/README.md +1 -1
  66. package/marketplace/skills/a11y/SKILL.md +1 -1
  67. package/marketplace/skills/best-practice/SKILL.md +211 -0
  68. package/marketplace/skills/context-graph/SKILL.md +1 -1
  69. package/marketplace/skills/debugging/SKILL.md +1 -1
  70. package/marketplace/skills/graph-audit/SKILL.md +3 -1
  71. package/marketplace/skills/postgres-rls/SKILL.md +284 -0
  72. package/marketplace/skills/refactor/SKILL.md +1 -1
  73. package/marketplace/skills/skill-infrastructure/SKILL.md +2 -0
  74. package/marketplace/skills/skill-router/SKILL.md +3 -1
  75. package/marketplace/skills/testing-strategy/SKILL.md +1 -1
  76. package/package.json +3 -1
  77. package/schemas/manifest.schema.json +8 -0
  78. package/schemas/manifest.v6.schema.json +8 -0
  79. package/schemas/skill.context.jsonld +5 -0
  80. package/schemas/skill.schema.json +27 -0
  81. package/scripts/__tests__/test-marketplace-export.js +6 -2
  82. package/scripts/__tests__/test-v3-1-alias-contract.js +3 -3
  83. package/scripts/build-status-doc.js +177 -0
  84. package/scripts/check-doc-drift.js +224 -0
  85. package/scripts/check-markdown-links.js +34 -4
  86. package/scripts/check-mirror-freeze.js +270 -0
  87. package/scripts/export-marketplace-skills.js +35 -6
  88. package/scripts/lib/audit-prompt-builder.js +3 -3
  89. package/scripts/lib/parse-frontmatter.js +2 -2
  90. package/scripts/skill-audit.js +7 -9
@@ -0,0 +1,284 @@
1
+ ---
2
+ name: postgres-rls
3
+ description: "Guides agents implementing or auditing PostgreSQL Row Level Security in a multi-tenant SaaS codebase. Covers ENABLE+FORCE pairing, USING+WITH CHECK policies, view bypass via security_invoker, SET LOCAL for connection pool safety, superuser/owner bypass detection, and materialized view risks. Do NOT use for application-level authorization logic (use nextauth-patterns), non-PostgreSQL databases, or application query tier questions (use multi-tenancy-rls for orgQuery() usage)."
4
+ license: MIT
5
+ compatibility: "Targets PostgreSQL 15+ with Neon serverless driver. The security_invoker view pattern requires PG15+. The SET LOCAL / set_config(key, val, true) pattern applies to any connection-pooled PostgreSQL setup. Core ENABLE+FORCE+USING+WITH CHECK rules are PG version-independent."
6
+ allowed-tools: Read Grep Glob Bash
7
+ metadata:
8
+ grounding: "{\"domain_object\":\"PostgreSQL Row Level Security implementation in Sales Hub\",\"grounding_mode\":\"repo_specific\",\"truth_sources\":[\"../sales-hub/apps/web/src/lib/db.ts\",\"../sales-hub/db/migrations/20260218_rls_hardening.sql\",\"../sales-hub/db/migrations/20260314_rls_expansion.sql\",\"../sales-hub/db/migrations/20260314_rls_with_check_and_security_invoker.sql\",\"../sales-hub/db/migrations/20260315_enable_rls_all.sql\"],\"failure_modes\":[\"security_leak\",\"incorrect_isolation\",\"missing_force_rls\",\"missing_with_check\",\"superuser_bypass\",\"connection_pool_context_leakage\",\"view_security_invoker_missing\"],\"evidence_priority\":\"repo_code_first\"}"
9
+ drift_check: "{\"last_verified\":\"2026-05-18\",\"truth_source_hashes\":{\"../sales-hub/apps/web/src/lib/db.ts\":\"d1d9988d1da4545c58f76b5f44dbd12d5323409b777920d31d5f496db31ad7cb\",\"../sales-hub/db/migrations/20260218_rls_hardening.sql\":\"b5fb6dc74e78a3fb7e77c669af4e4b4303a4e995eaf4993cb6bdcbe7bdca424f\",\"../sales-hub/db/migrations/20260314_rls_expansion.sql\":\"406e2c419b09a6866f10c508e4ff6dc1f7a1ee82b788c5e2515186561c30ffb5\",\"../sales-hub/db/migrations/20260314_rls_with_check_and_security_invoker.sql\":\"2edcb51debd4ab4e7306eb6f8908c7a725217b19962e1429728e911fc57e88fb\",\"../sales-hub/db/migrations/20260315_enable_rls_all.sql\":\"c8de6823684f84b54376284a3a51bb6b52e38acd4f5c29664be26e89e5d39570\"}}"
10
+ metadata: "{\"schema_version\":6,\"version\":\"1.1.0\",\"type\":\"capability\",\"category\":\"engineering\",\"domain\":\"engineering/database-security\",\"scope\":\"codebase\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-05-18\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-05-18\\\\\\\",\\\\\\\"truth_source_hashes\\\\\\\":{\\\\\\\"sales-hub/apps/web/src/lib/db.ts\\\\\\\":\\\\\\\"d1d9988d1da4545c58f76b5f44dbd12d5323409b777920d31d5f496db31ad7cb\\\\\\\",\\\\\\\"sales-hub/db/migrations/20260218_rls_hardening.sql\\\\\\\":\\\\\\\"b5fb6dc74e78a3fb7e77c669af4e4b4303a4e995eaf4993cb6bdcbe7bdca424f\\\\\\\",\\\\\\\"sales-hub/db/migrations/20260314_rls_expansion.sql\\\\\\\":\\\\\\\"406e2c419b09a6866f10c508e4ff6dc1f7a1ee82b788c5e2515186561c30ffb5\\\\\\\",\\\\\\\"sales-hub/db/migrations/20260314_rls_with_check_and_security_invoker.sql\\\\\\\":\\\\\\\"2edcb51debd4ab4e7306eb6f8908c7a725217b19962e1429728e911fc57e88fb\\\\\\\",\\\\\\\"sales-hub/db/migrations/20260315_enable_rls_all.sql\\\\\\\":\\\\\\\"c8de6823684f84b54376284a3a51bb6b52e38acd4f5c29664be26e89e5d39570\\\\\\\"}}\",\"eval_artifacts\":\"none\",\"eval_state\":\"unverified\",\"routing_eval\":\"absent\",\"stability\":\"stable\",\"keywords\":\"[\\\\\\\"rls\\\\\\\",\\\\\\\"row-level-security\\\\\\\",\\\\\\\"postgres\\\\\\\",\\\\\\\"tenant-isolation\\\\\\\",\\\\\\\"org-id\\\\\\\",\\\\\\\"security-policy\\\\\\\",\\\\\\\"set-local\\\\\\\",\\\\\\\"view-bypass\\\\\\\",\\\\\\\"migration\\\\\\\",\\\\\\\"multi-tenant\\\\\\\",\\\\\\\"ENABLE ROW LEVEL SECURITY\\\\\\\",\\\\\\\"FORCE ROW LEVEL SECURITY\\\\\\\",\\\\\\\"security_invoker\\\\\\\",\\\\\\\"USING clause\\\\\\\",\\\\\\\"WITH CHECK\\\\\\\",\\\\\\\"superuser bypass\\\\\\\",\\\\\\\"orgQuery\\\\\\\",\\\\\\\"app.organization_id\\\\\\\",\\\\\\\"connection pool safety\\\\\\\",\\\\\\\"materialized view RLS\\\\\\\"]\",\"examples\":\"[\\\\\\\"adding RLS to a new table — what statements are required and in what order?\\\\\\\",\\\\\\\"auditing our existing RLS migrations for missing FORCE or WITH CHECK\\\\\\\",\\\\\\\"implementing a view on an RLS-protected table in PostgreSQL 15+\\\\\\\",\\\\\\\"debugging a cross-tenant data leak in a multi-tenant SaaS system\\\\\\\",\\\\\\\"ensuring connection pool context (SET LOCAL) does not leak tenant state between requests\\\\\\\",\\\\\\\"reviewing a migration that enables RLS to check for superuser and owner bypass risks\\\\\\\",\\\\\\\"verifying that materialized views over RLS tables are documented as security-sensitive\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"implementing application-level auth guards (requireAuth, requireOrgAuth) — use nextauth-patterns\\\\\\\",\\\\\\\"choosing when to use orgQuery() vs query() in application code — use multi-tenancy-rls\\\\\\\",\\\\\\\"implementing CSRF protection or webhook HMAC verification — use security-scanning\\\\\\\",\\\\\\\"designing the overall multi-tenant data architecture — use data-architect\\\\\\\",\\\\\\\"designing row-level access control in non-PostgreSQL databases\\\\\\\"]\",\"relations\":\"{\\\\\\\"adjacent\\\\\\\":[\\\\\\\"database-migration\\\\\\\",\\\\\\\"security-scanning\\\\\\\",\\\\\\\"data-architect\\\\\\\",\\\\\\\"guardrails\\\\\\\"],\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"nextauth-patterns\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"nextauth-patterns owns application-level authorization (requireAuth, requireOrgAuth, withOrgAuth); postgres-rls owns database-level isolation via PostgreSQL RLS policies\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"multi-tenancy-rls\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"multi-tenancy-rls owns the application query tier (orgQuery, query, withAppSession usage patterns); postgres-rls owns the PostgreSQL server-side RLS policy implementation\\\\\\\"}],\\\\\\\"verify_with\\\\\\\":[\\\\\\\"security-scanning\\\\\\\",\\\\\\\"database-migration\\\\\\\"]}\",\"grounding\":\"{\\\\\\\"domain_object\\\\\\\":\\\\\\\"PostgreSQL Row Level Security implementation in Sales Hub\\\\\\\",\\\\\\\"grounding_mode\\\\\\\":\\\\\\\"repo_specific\\\\\\\",\\\\\\\"truth_sources\\\\\\\":[\\\\\\\"sales-hub/apps/web/src/lib/db.ts\\\\\\\",\\\\\\\"sales-hub/db/migrations/20260218_rls_hardening.sql\\\\\\\",\\\\\\\"sales-hub/db/migrations/20260314_rls_expansion.sql\\\\\\\",\\\\\\\"sales-hub/db/migrations/20260314_rls_with_check_and_security_invoker.sql\\\\\\\",\\\\\\\"sales-hub/db/migrations/20260315_enable_rls_all.sql\\\\\\\"],\\\\\\\"failure_modes\\\\\\\":[\\\\\\\"security_leak\\\\\\\",\\\\\\\"incorrect_isolation\\\\\\\",\\\\\\\"missing_force_rls\\\\\\\",\\\\\\\"missing_with_check\\\\\\\",\\\\\\\"superuser_bypass\\\\\\\",\\\\\\\"connection_pool_context_leakage\\\\\\\",\\\\\\\"view_security_invoker_missing\\\\\\\"],\\\\\\\"evidence_priority\\\\\\\":\\\\\\\"repo_code_first\\\\\\\"}\",\"portability\":\"{\\\\\\\"readiness\\\\\\\":\\\\\\\"declared\\\\\\\",\\\\\\\"targets\\\\\\\":[\\\\\\\"skill-md\\\\\\\"]}\",\"lifecycle\":\"{\\\\\\\"stale_after_days\\\\\\\":90,\\\\\\\"review_cadence\\\\\\\":\\\\\\\"quarterly\\\\\\\"}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v6\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/postgres-rls/SKILL.md\"}"
11
+ skill_graph_source_repo: "https://github.com/jacob-balslev/skill-graph"
12
+ skill_graph_protocol: Skill Metadata Protocol v4
13
+ skill_graph_project: Skill Graph
14
+ skill_graph_canonical_skill: skills/postgres-rls/SKILL.md
15
+ ---
16
+
17
+ # PostgreSQL Row Level Security
18
+
19
+ ## Domain Context
20
+
21
+ **What is postgres-rls?** Guides agents implementing or auditing PostgreSQL Row Level Security in Sales Hub. Covers ENABLE+FORCE pairing, USING+WITH CHECK policies, view bypass via security_invoker, SET LOCAL for connection pool safety, superuser/owner bypass detection, and materialized view risks.
22
+
23
+ ## Key Files
24
+
25
+ | File | Purpose |
26
+ |------|------|
27
+ | `apps/web/src/lib/db.ts` | orgQuery(), query(), withReportingCurrency(), withAppSession() wrapper pattern (lines 1-895) |
28
+ | `db/migrations/20260218_rls_hardening.sql` | RLS hardening migration — ENABLE+FORCE patterns |
29
+ | `db/migrations/20260314_rls_expansion.sql` | RLS expansion to additional tables |
30
+ | `db/migrations/20260314_rls_with_check_and_security_invoker.sql` | WITH CHECK and security_invoker additions |
31
+ | `db/migrations/20260315_enable_rls_all.sql` | Full RLS enablement across all tables |
32
+
33
+ ## Project-Specific Rules
34
+
35
+ This skill captures project-local rules and constraints that narrow or refine a broader pattern. Treat the repo-specific guidance and key files below as authoritative for this project.
36
+
37
+ Sales Hub uses `org_id` (not `tenant_id`) as the tenant column. Session variable is `app.organization_id`. All application queries go through `orgQuery()` (auto-sets session variable) or `query()` (system-only, bypasses RLS). The `db.ts` module manages a connection pool via `pg.Pool`; RLS context is set per-transaction using `set_config('app.organization_id', orgId, true)` (the third parameter `true` makes it transaction-local, equivalent to `SET LOCAL`).
38
+
39
+ ## Coverage
40
+
41
+ This skill covers PostgreSQL Row Level Security implementation and auditing in Sales Hub: ENABLE+FORCE pairing, USING+WITH CHECK policies, view bypass via security_invoker, SET LOCAL for connection pool safety, superuser/owner bypass detection, materialized view risks, the `orgQuery()` wrapper pattern, and the `app.organization_id` session variable convention.
42
+
43
+ ## Philosophy
44
+
45
+ RLS is the last line of defense against cross-tenant data leaks. When agents implement it incorrectly (missing FORCE, missing WITH CHECK, using SET instead of SET LOCAL), the system appears to work in testing but silently leaks data in production. This skill captures the exact footguns that have caused or nearly caused data breaches in multi-tenant SaaS systems.
46
+
47
+ ## Overview
48
+
49
+ Row Level Security (RLS) provides defense-in-depth for data isolation. When implemented correctly, it prevents data leaks even if application code misses a filter. When implemented incorrectly, it creates false security confidence while data bleeds between tenants.
50
+
51
+ **Core principle:** RLS is your last line of defense, not your only one. Get it wrong and you have a data breach.
52
+
53
+ **Announce at start:** "I'm applying postgres-rls to verify Row Level Security implementation."
54
+
55
+ ## When This Skill Applies
56
+
57
+ This skill is MANDATORY when ANY of these patterns are touched:
58
+
59
+ | Pattern | Examples |
60
+ |---------|----------|
61
+ | `**/migrations/**/*tenant*` | migrations/001_add_tenant_id.sql |
62
+ | `**/migrations/**/*rls*` | migrations/005_enable_rls.sql |
63
+ | `**/migrations/**/*policy*` | migrations/010_create_policies.sql |
64
+ | `**/*policy*.sql` | db/policies.sql |
65
+ | `**/auth/**` | src/auth/context.ts |
66
+ | `**/*tenant*` | lib/tenant.ts, services/tenantService.ts |
67
+ | `**/*multi-tenant*` | docs/multi-tenant-architecture.md |
68
+
69
+ Check with:
70
+ ```bash
71
+ git diff --name-only HEAD~1 | grep -iE '(tenant|rls|policy|auth.*sql|multi.?tenant)'
72
+ ```
73
+
74
+ ## Sales Hub Specifics
75
+
76
+ Sales Hub uses `org_id` (not `tenant_id`) as the tenant column. Session variable is `app.organization_id`.
77
+
78
+ **Current state (2026-03-14):** 22 tables with RLS. Policies use:
79
+ ```sql
80
+ CREATE POLICY {table}_org_isolation ON {table}
81
+ FOR ALL
82
+ USING (org_id IS NOT NULL AND org_id = (current_setting('app.organization_id', true))::uuid)
83
+ WITH CHECK (org_id IS NOT NULL AND org_id = (current_setting('app.organization_id', true))::uuid);
84
+ ```
85
+
86
+ **Query wrappers (in `apps/web/src/lib/db.ts` — orgQuery, query, withAppSession):**
87
+ - `orgQuery(orgId, sql, params)` — auto-sets session variable + validates (preferred)
88
+ - `query(sql, params)` — raw query without validation (system operations only)
89
+ - `withAppSession(settings, fn)` — manual session variable injection
90
+
91
+ **Views:** All views use `ALTER VIEW ... SET (security_invoker = true)` (PG15+).
92
+
93
+ ## The Critical Vulnerabilities
94
+
95
+ ### 1. Superuser Bypass (CRITICAL)
96
+
97
+ Superusers and roles with `BYPASSRLS` ignore ALL policies.
98
+
99
+ ```sql
100
+ -- DANGEROUS: Testing as superuser shows RLS "working" when it's bypassed
101
+ SET ROLE postgres;
102
+ SELECT * FROM orders; -- Returns ALL rows, RLS ignored
103
+
104
+ -- CORRECT: Test as application role
105
+ SET ROLE app_user;
106
+ SELECT * FROM orders; -- Returns only permitted rows
107
+ ```
108
+
109
+ **Checklist:**
110
+ - [ ] Application connects as non-superuser role
111
+ - [ ] No roles have `BYPASSRLS` attribute
112
+ - [ ] Tests run as application role, NOT superuser
113
+
114
+ ### 2. Table Owner Bypass (CRITICAL)
115
+
116
+ Table owners bypass RLS unless `FORCE ROW LEVEL SECURITY` is set.
117
+
118
+ ```sql
119
+ -- INCOMPLETE: Owners bypass this
120
+ ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
121
+
122
+ -- COMPLETE: Everyone including owners must obey policies
123
+ ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
124
+ ALTER TABLE orders FORCE ROW LEVEL SECURITY;
125
+ ```
126
+
127
+ **Checklist:**
128
+ - [ ] All RLS tables have both ENABLE and FORCE
129
+ - [ ] Migration includes both statements
130
+
131
+ ### 3. View Bypass (CRITICAL)
132
+
133
+ Views run with creator's privileges by default. Views owned by superusers bypass RLS entirely.
134
+
135
+ ```sql
136
+ -- DANGEROUS: View owned by superuser bypasses RLS
137
+ CREATE VIEW all_orders AS SELECT * FROM orders;
138
+
139
+ -- SAFE (PostgreSQL 15+): Security invoker respects caller's RLS
140
+ CREATE VIEW user_orders
141
+ WITH (security_invoker = true)
142
+ AS SELECT * FROM orders;
143
+
144
+ -- Or retrofit existing views:
145
+ ALTER VIEW user_orders SET (security_invoker = true);
146
+ ```
147
+
148
+ **Checklist:**
149
+ - [ ] All views on RLS tables use `security_invoker = true` (PG15+)
150
+ - [ ] Views not owned by superuser roles
151
+ - [ ] Materialized views documented as bypassing RLS
152
+
153
+ ### 4. USING vs WITH CHECK Mismatch (HIGH)
154
+
155
+ `USING` filters reads; `WITH CHECK` validates writes. Missing `WITH CHECK` allows inserting data you can't see.
156
+
157
+ ```sql
158
+ -- INCOMPLETE: User can INSERT rows they can't SELECT
159
+ CREATE POLICY tenant_isolation ON orders
160
+ USING (tenant_id = current_setting('app.tenant_id')::uuid);
161
+
162
+ -- COMPLETE: Both read and write protected
163
+ CREATE POLICY tenant_isolation ON orders
164
+ USING (tenant_id = current_setting('app.tenant_id')::uuid)
165
+ WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);
166
+ ```
167
+
168
+ **Checklist:**
169
+ - [ ] All policies have both USING and WITH CHECK
170
+ - [ ] WITH CHECK logic matches security intent
171
+
172
+ ### 5. Thread-Local Context Leakage (HIGH)
173
+
174
+ Connection pooling can leak tenant context between requests.
175
+
176
+ ```sql
177
+ -- DANGEROUS: Context persists across pooled connections
178
+ SET app.tenant_id = 'tenant-123';
179
+
180
+ -- SAFE: Use SET LOCAL inside transaction (auto-resets)
181
+ BEGIN;
182
+ SET LOCAL app.tenant_id = 'tenant-123';
183
+ -- ... queries ...
184
+ COMMIT; -- Context automatically cleared
185
+ ```
186
+
187
+ **Application pattern:**
188
+ ```typescript
189
+ // DANGEROUS: Leaks between requests
190
+ await db.query(`SET app.tenant_id = '${tenantId}'`);
191
+
192
+ // SAFE: Transaction-scoped context (Sales Hub pattern)
193
+ await client.query("SELECT set_config('app.organization_id', $1, true)", [orgId]);
194
+ // The `true` parameter = transaction-local, equivalent to SET LOCAL
195
+ ```
196
+
197
+ **Checklist:**
198
+ - [ ] Always use `SET LOCAL` or `set_config(key, val, true)`
199
+ - [ ] Context set inside transactions
200
+ - [ ] Post-request handler resets context (defense in depth)
201
+
202
+ ### 6. SQL Injection in Policy Functions (HIGH)
203
+
204
+ Functions used in policies can be injection vectors.
205
+
206
+ ```sql
207
+ -- The function must be injection-safe:
208
+ CREATE OR REPLACE FUNCTION current_tenant()
209
+ RETURNS uuid AS $$
210
+ BEGIN
211
+ -- SAFE: Casts to UUID, not string concatenation
212
+ RETURN current_setting('app.tenant_id')::uuid;
213
+ END;
214
+ $$ LANGUAGE plpgsql STABLE;
215
+ ```
216
+
217
+ ### 7. Materialized Views and Data Export (MEDIUM)
218
+
219
+ Materialized views don't respect source table RLS. Data exports may bypass policies.
220
+
221
+ **Checklist:**
222
+ - [ ] Materialized views documented as security-sensitive
223
+ - [ ] Export jobs run as application role
224
+ - [ ] Audit log for bulk data access
225
+
226
+ ## Migration Pattern
227
+
228
+ ### Safe RLS Migration
229
+
230
+ ```sql
231
+ -- Step 1: Add column (if needed)
232
+ ALTER TABLE orders ADD COLUMN IF NOT EXISTS org_id uuid REFERENCES organizations(id);
233
+
234
+ -- Step 2: Backfill data (batched for large tables)
235
+ UPDATE orders SET org_id = (...) WHERE org_id IS NULL;
236
+
237
+ -- Step 3: Create index
238
+ CREATE INDEX IF NOT EXISTS idx_orders_org_id ON orders(org_id);
239
+
240
+ -- Step 4: Enable RLS (both statements!)
241
+ ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
242
+ ALTER TABLE orders FORCE ROW LEVEL SECURITY;
243
+
244
+ -- Step 5: Create policies with USING and WITH CHECK
245
+ DROP POLICY IF EXISTS orders_org_isolation ON orders;
246
+ CREATE POLICY orders_org_isolation ON orders
247
+ FOR ALL
248
+ USING (org_id IS NOT NULL AND org_id = (current_setting('app.organization_id', true))::uuid)
249
+ WITH CHECK (org_id IS NOT NULL AND org_id = (current_setting('app.organization_id', true))::uuid);
250
+
251
+ -- Step 6: Verify
252
+ DO $$ BEGIN
253
+ IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename = 'orders' AND policyname = 'orders_org_isolation') THEN
254
+ RAISE EXCEPTION 'ASSERTION FAILED: RLS policy not found on orders';
255
+ END IF;
256
+ END; $$;
257
+ ```
258
+
259
+ ## Verification
260
+
261
+ Before completing RLS implementation:
262
+
263
+ - [ ] All tables have ENABLE and FORCE ROW LEVEL SECURITY
264
+ - [ ] All policies have both USING and WITH CHECK
265
+ - [ ] Application connects as non-superuser, non-BYPASSRLS role
266
+ - [ ] Context set with SET LOCAL / set_config(key, val, true) inside transactions
267
+ - [ ] Views use security_invoker = true (PG15+)
268
+ - [ ] Policy columns indexed
269
+ - [ ] Cross-tenant isolation tests passing
270
+
271
+ ## References
272
+
273
+ - [PostgreSQL RLS Documentation](https://www.postgresql.org/docs/current/ddl-rowsecurity.html)
274
+ - [Common RLS Footguns](https://www.bytebase.com/blog/postgres-row-level-security-footguns/)
275
+ - [RLS Performance Optimization](https://scottpierce.dev/posts/optimizing-postgres-rls/)
276
+
277
+
278
+ ## Do NOT Use When
279
+
280
+ | Instead of this skill | Use | Why |
281
+ |---|---|---|
282
+ | Application-level auth logic | `nextauth-patterns` | RLS is DB-level; app auth is a separate layer |
283
+ | Non-PostgreSQL databases | General security patterns | RLS is PostgreSQL-specific |
284
+ | orgQuery() wrapper usage questions | `multi-tenancy-rls` | That skill owns the application query tier |
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: "Markdown, Git, any codebase"
6
6
  allowed-tools: Read Grep Bash
7
7
  metadata:
8
- metadata: "{\"schema_version\":6,\"version\":\"1.0.0\",\"type\":\"workflow\",\"category\":\"engineering\",\"scope\":\"portable\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-04-18\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-04-18\\\\\\\"}\",\"eval_artifacts\":\"present\",\"eval_state\":\"passing\",\"routing_eval\":\"present\",\"stability\":\"experimental\",\"keywords\":\"[\\\\\\\"refactor\\\\\\\",\\\\\\\"cleanup\\\\\\\",\\\\\\\"simplify\\\\\\\",\\\\\\\"extract function\\\\\\\",\\\\\\\"reduce duplication\\\\\\\",\\\\\\\"clean this up\\\\\\\",\\\\\\\"simplify this\\\\\\\",\\\\\\\"rename this\\\\\\\",\\\\\\\"split this file\\\\\\\",\\\\\\\"too long function\\\\\\\",\\\\\\\"duplicated logic\\\\\\\",\\\\\\\"decompose function\\\\\\\",\\\\\\\"decompose code\\\\\\\",\\\\\\\"decompose long\\\\\\\",\\\\\\\"split by responsibility\\\\\\\",\\\\\\\"behavior preserving\\\\\\\",\\\\\\\"rename module\\\\\\\",\\\\\\\"rename utils\\\\\\\",\\\\\\\"messy code\\\\\\\",\\\\\\\"messy suite\\\\\\\",\\\\\\\"extract helper\\\\\\\",\\\\\\\"extract duplicated\\\\\\\",\\\\\\\"consolidate logic\\\\\\\",\\\\\\\"tighten structure\\\\\\\",\\\\\\\"large component refactor\\\\\\\",\\\\\\\"make sure refactor preserves behavior\\\\\\\"]\",\"triggers\":\"[\\\\\\\"refactor-skill\\\\\\\"]\",\"examples\":\"[\\\\\\\"this 600-line function is hard to reason about — decompose it while keeping tests green\\\\\\\",\\\\\\\"extract the duplicated validation logic from these three handlers into a helper\\\\\\\",\\\\\\\"rename this module from `utils` to something that describes what it actually does\\\\\\\",\\\\\\\"split this file by responsibility; no behavior changes, tests must still pass\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"the test is failing after my edit — what did I break?\\\\\\\",\\\\\\\"write an architecture note explaining this pattern for new team members\\\\\\\",\\\\\\\"reproduce why this function retries three times on transient network errors\\\\\\\"]\",\"relations\":\"{\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"debugging\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"debugging chases an observed failure; refactor runs only with a green test suite and preserves behavior\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"tool-call-flow\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"tool-call-flow owns the protocol-level cycle of model→runtime tool invocation including retry encoding inside the cycle; refactor only restructures the surrounding code while preserving behavior. The anti_example about a function retrying three times is a tool-call/runtime concern, not a refactor concern.\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"generative-ui\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"generative-ui owns the model-emits-typed-UI-spec pattern; refactor only restructures existing code while preserving behavior. The retries anti_example has token overlap with the model-output/runtime cycle vocabulary generative-ui discusses, so naming it here keeps the boundary explicit.\\\\\\\"}],\\\\\\\"verify_with\\\\\\\":[\\\\\\\"testing-strategy\\\\\\\"],\\\\\\\"depends_on\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"testing-strategy\\\\\\\",\\\\\\\"min_version\\\\\\\":\\\\\\\"^1.0.0\\\\\\\"}]}\",\"portability\":\"{\\\\\\\"readiness\\\\\\\":\\\\\\\"scripted\\\\\\\",\\\\\\\"targets\\\\\\\":[\\\\\\\"skill-md\\\\\\\"]}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v5\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/refactor/SKILL.md\"}"
8
+ metadata: "{\"schema_version\":6,\"version\":\"1.0.0\",\"type\":\"workflow\",\"category\":\"engineering\",\"scope\":\"portable\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-04-18\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-04-18\\\\\\\"}\",\"eval_artifacts\":\"present\",\"eval_state\":\"passing\",\"routing_eval\":\"present\",\"stability\":\"experimental\",\"keywords\":\"[\\\\\\\"refactor\\\\\\\",\\\\\\\"cleanup\\\\\\\",\\\\\\\"simplify\\\\\\\",\\\\\\\"extract function\\\\\\\",\\\\\\\"reduce duplication\\\\\\\",\\\\\\\"clean this up\\\\\\\",\\\\\\\"simplify this\\\\\\\",\\\\\\\"rename this\\\\\\\",\\\\\\\"split this file\\\\\\\",\\\\\\\"too long function\\\\\\\",\\\\\\\"duplicated logic\\\\\\\",\\\\\\\"decompose function\\\\\\\",\\\\\\\"decompose code\\\\\\\",\\\\\\\"decompose long\\\\\\\",\\\\\\\"split by responsibility\\\\\\\",\\\\\\\"behavior preserving\\\\\\\",\\\\\\\"rename module\\\\\\\",\\\\\\\"rename utils\\\\\\\",\\\\\\\"messy code\\\\\\\",\\\\\\\"messy suite\\\\\\\",\\\\\\\"extract helper\\\\\\\",\\\\\\\"extract duplicated\\\\\\\",\\\\\\\"consolidate logic\\\\\\\",\\\\\\\"tighten structure\\\\\\\",\\\\\\\"large component refactor\\\\\\\",\\\\\\\"make sure refactor preserves behavior\\\\\\\"]\",\"triggers\":\"[\\\\\\\"refactor-skill\\\\\\\"]\",\"examples\":\"[\\\\\\\"this 600-line function is hard to reason about — decompose it while keeping tests green\\\\\\\",\\\\\\\"extract the duplicated validation logic from these three handlers into a helper\\\\\\\",\\\\\\\"rename this module from `utils` to something that describes what it actually does\\\\\\\",\\\\\\\"split this file by responsibility; no behavior changes, tests must still pass\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"the test is failing after my edit — what did I break?\\\\\\\",\\\\\\\"write an architecture note explaining this pattern for new team members\\\\\\\",\\\\\\\"reproduce why this function retries three times on transient network errors\\\\\\\"]\",\"relations\":\"{\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"debugging\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"debugging chases an observed failure; refactor runs only with a green test suite and preserves behavior\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"tool-call-flow\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"tool-call-flow owns the protocol-level cycle of model→runtime tool invocation including retry encoding inside the cycle; refactor only restructures the surrounding code while preserving behavior. The anti_example about a function retrying three times is a tool-call/runtime concern, not a refactor concern.\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"generative-ui\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"generative-ui owns the model-emits-typed-UI-spec pattern; refactor only restructures existing code while preserving behavior. The retries anti_example has token overlap with the model-output/runtime cycle vocabulary generative-ui discusses, so naming it here keeps the boundary explicit.\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"context-graph\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"context-graph owns the design and documentation of AI workspace graph architecture; refactor owns behavior-preserving code restructuring. Writing an architecture note for team members is documentation work, not a code refactor.\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"error-boundary\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"error-boundary owns React error boundary design and the mechanics of retry-on-error patterns; refactor owns behavior-preserving code restructuring. Reproducing or explaining retry behavior is a runtime/error-handling concern, not a refactor concern.\\\\\\\"}],\\\\\\\"verify_with\\\\\\\":[\\\\\\\"testing-strategy\\\\\\\"],\\\\\\\"depends_on\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"testing-strategy\\\\\\\",\\\\\\\"min_version\\\\\\\":\\\\\\\"^1.0.0\\\\\\\"}]}\",\"portability\":\"{\\\\\\\"readiness\\\\\\\":\\\\\\\"scripted\\\\\\\",\\\\\\\"targets\\\\\\\":[\\\\\\\"skill-md\\\\\\\"]}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v5\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/refactor/SKILL.md\"}"
9
9
  skill_graph_source_repo: "https://github.com/jacob-balslev/skill-graph"
10
10
  skill_graph_protocol: Skill Metadata Protocol v4
11
11
  skill_graph_project: Skill Graph
@@ -5,6 +5,8 @@ license: MIT
5
5
  compatibility: "Library- and harness-agnostic. Patterns apply to any skill-style library (Skill Graph, Claude skills, Cursor rules, custom in-house skill systems). Specific tool names in this skill (skill-lint, generate-manifest, routing-eval, drift-sentinel) are concrete examples from the Skill Graph reference implementation -- substitute your library's equivalents."
6
6
  allowed-tools: Read Grep Bash Edit Write
7
7
  metadata:
8
+ grounding: "{\"domain_object\":\"Deterministic health tooling for Skill Graph libraries\",\"grounding_mode\":\"hybrid\",\"truth_sources\":[\"package.json\",\"bin/skill-graph.js\",\"scripts/skill-lint.js\",\"scripts/lib/roots.js\",\"scripts/check-protocol-consistency.js\",\"scripts/generate-manifest.js\",\"scripts/skill-graph-drift.js\",\"scripts/skill-overlap.js\",\"scripts/skill-graph-routing-eval.js\",\"docs/manifest-field-mapping.md\"],\"failure_modes\":[\"health_tooling_categories_missing_from_ci\",\"protocol_mapping_drift\",\"eval_thresholds_become_self_attested\",\"overlap_or_drift_checks_not_run_after_batch_changes\"],\"evidence_priority\":\"repo_code_first\"}"
9
+ drift_check: "{\"last_verified\":\"2026-05-18\",\"truth_source_hashes\":{\"package.json\":\"2f480e50b8eecaa022caf065b5bb98db5db407f4e3ff8553a092a3f70750edba\",\"bin/skill-graph.js\":\"3048b6e2d9e648a25efa38152578217eaf230716c1dabc921e8f8d944164ec8b\",\"scripts/skill-lint.js\":\"e5de8a822b88172079263c8316b173e688b71498c9ed6a8a54dd0fba6aa9fd66\",\"scripts/lib/roots.js\":\"49085fc54b2c6ff0ad23a2dffe25b5ab2b3d1e8d14a8d5b1e1eefb53a30f20de\",\"scripts/check-protocol-consistency.js\":\"22f1f747b6b578e83ae371ac3f9af4b6906d94529f383d1785ed3303b4c5a008\",\"scripts/generate-manifest.js\":\"ec4ad89e21e44c272676846377679f59a272c193f0b0d448a7b6d881b0b9effc\",\"scripts/skill-graph-drift.js\":\"350f624a6e82bb488cd9abd3be4d832ca7892ce1c7f27d39efd97326e1f04db6\",\"scripts/skill-overlap.js\":\"ed642cbc677cc76ec1321300b37d6752337b6b5541c7a9f558fd315d6f934e4b\",\"scripts/skill-graph-routing-eval.js\":\"fffac2858863662bde6bc54c56bb77a219ae93f626e0c8d5886566f998181deb\",\"docs/manifest-field-mapping.md\":\"8de06aa16e23c219da1f5f2ec38b5b29aa912ad577377c8422e2300cdd77ee90\"}}"
8
10
  metadata: "{\"schema_version\":6,\"version\":\"1.0.0\",\"type\":\"capability\",\"category\":\"agent\",\"domain\":\"agent/skill-system\",\"scope\":\"portable\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-05-13\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-05-13\\\\\\\",\\\\\\\"truth_source_hashes\\\\\\\":{\\\\\\\"package.json\\\\\\\":\\\\\\\"7a3410a004aea78a2065092e289c0f3cf3c082298804dda6c5829eff22c14b62\\\\\\\",\\\\\\\"bin/skill-graph.js\\\\\\\":\\\\\\\"113a1e01ac7276ac1b5d77a1c32e35a73113da93fc33cfd0caf6db842d2d679f\\\\\\\",\\\\\\\"scripts/skill-lint.js\\\\\\\":\\\\\\\"3a78f75f8921542b91dc619cd41bde29bf379de3c16bdcf3653c854ecbe9fa29\\\\\\\",\\\\\\\"scripts/lib/roots.js\\\\\\\":\\\\\\\"e742efa57b6c33ff1c87034b16a689d1499f6d53c1e6b740f3e9783db7fd557f\\\\\\\",\\\\\\\"scripts/check-protocol-consistency.js\\\\\\\":\\\\\\\"0ff39406d36e7a9e51c176f657f4f426d8bd5a3fe6411d28b9e9a93dc7d89f29\\\\\\\",\\\\\\\"scripts/generate-manifest.js\\\\\\\":\\\\\\\"9d7bbbdae440fdb1763d61ffa7bda10c9efae92359d1c2139d0e971582d59e0e\\\\\\\",\\\\\\\"scripts/skill-graph-drift.js\\\\\\\":\\\\\\\"6b69c25b59c16b477a377e5ab40adb6ff30f72d5a12947772053a6cd16b1f409\\\\\\\",\\\\\\\"scripts/skill-overlap.js\\\\\\\":\\\\\\\"ed642cbc677cc76ec1321300b37d6752337b6b5541c7a9f558fd315d6f934e4b\\\\\\\",\\\\\\\"scripts/skill-graph-routing-eval.js\\\\\\\":\\\\\\\"fffac2858863662bde6bc54c56bb77a219ae93f626e0c8d5886566f998181deb\\\\\\\",\\\\\\\"docs/manifest-field-mapping.md\\\\\\\":\\\\\\\"aca0b7f2d4631be24a3e7daed1a1d207b488f253164a7d514b9db7af21c6177f\\\\\\\"}}\",\"eval_artifacts\":\"planned\",\"eval_state\":\"unverified\",\"routing_eval\":\"absent\",\"stability\":\"experimental\",\"keywords\":\"[\\\\\\\"skill library health\\\\\\\",\\\\\\\"skill system tooling\\\\\\\",\\\\\\\"skill library decay\\\\\\\",\\\\\\\"skill library maintenance\\\\\\\",\\\\\\\"skill census\\\\\\\",\\\\\\\"skill inventory\\\\\\\",\\\\\\\"frontmatter validation\\\\\\\",\\\\\\\"imperative conflict\\\\\\\",\\\\\\\"skill overlap detection\\\\\\\",\\\\\\\"skill conflict\\\\\\\",\\\\\\\"routing gap\\\\\\\",\\\\\\\"routing miss\\\\\\\",\\\\\\\"routing health\\\\\\\",\\\\\\\"eval threshold\\\\\\\",\\\\\\\"eval minimum\\\\\\\",\\\\\\\"contradiction check\\\\\\\",\\\\\\\"negative expectation\\\\\\\",\\\\\\\"drift sentinel\\\\\\\",\\\\\\\"truth source hash\\\\\\\",\\\\\\\"mirror parity\\\\\\\",\\\\\\\"skill graph health\\\\\\\",\\\\\\\"production skill library\\\\\\\",\\\\\\\"skill linter\\\\\\\",\\\\\\\"skill quality gate\\\\\\\",\\\\\\\"phantom ref\\\\\\\"]\",\"examples\":\"[\\\\\\\"our skill library is growing and we're getting silent decay — eval counts dropping, conflicts emerging — what tooling should we add?\\\\\\\",\\\\\\\"two of our skills give opposite instructions for the same function — how do we detect this automatically?\\\\\\\",\\\\\\\"we keep getting skill-router misses on real user queries — how do we surface and close routing gaps?\\\\\\\",\\\\\\\"design a health-check pipeline for a 200-skill library that runs in CI\\\\\\\",\\\\\\\"what's a reasonable minimum eval count per skill, and how do we enforce it?\\\\\\\",\\\\\\\"our skill mirror in `.claude/skills` keeps drifting from the source — what's the parity check?\\\\\\\",\\\\\\\"we want to add a contradiction-check eval pattern — what does it look like and when do we use it?\\\\\\\",\\\\\\\"skill-overlap-detector flagged 12 imperative conflicts — how do we triage which to fix vs suppress?\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"scaffold a new SKILL.md for our team's deploy procedure\\\\\\\",\\\\\\\"audit this Skill Graph repo for schema conformance and dangling relation targets\\\\\\\",\\\\\\\"the manifest sample drifted from the generator — find the mismatch\\\\\\\",\\\\\\\"improve this prompt's wording to get better outputs\\\\\\\",\\\\\\\"review this AI-generated PR for correctness\\\\\\\",\\\\\\\"set up ESLint for our TypeScript repo\\\\\\\",\\\\\\\"draft an architecture note explaining why we chose Postgres\\\\\\\"]\",\"relations\":\"{\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"skill-scaffold\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"skill-scaffold owns authoring methodology for one new SKILL.md; skill-infrastructure owns the deterministic health-tooling layer that watches the entire library after authoring\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"graph-audit\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"graph-audit is the operational audit of one specific library (Skill Graph), scope: codebase; skill-infrastructure is the portable discipline of designing health tooling for any skill library\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"lint-overlay\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"lint-overlay covers lint-rule selection and gate placement for general codebases; skill-infrastructure covers the skill-system-specific tooling category that includes lint but extends to overlap, routing-gap, drift, and mirror-parity\\\\\\\"}],\\\\\\\"related\\\\\\\":[\\\\\\\"skill-scaffold\\\\\\\",\\\\\\\"graph-audit\\\\\\\",\\\\\\\"testing-strategy\\\\\\\"],\\\\\\\"verify_with\\\\\\\":[\\\\\\\"testing-strategy\\\\\\\",\\\\\\\"code-review\\\\\\\"]}\",\"grounding\":\"{\\\\\\\"domain_object\\\\\\\":\\\\\\\"Deterministic health tooling for Skill Graph libraries\\\\\\\",\\\\\\\"grounding_mode\\\\\\\":\\\\\\\"hybrid\\\\\\\",\\\\\\\"truth_sources\\\\\\\":[\\\\\\\"package.json\\\\\\\",\\\\\\\"bin/skill-graph.js\\\\\\\",\\\\\\\"scripts/skill-lint.js\\\\\\\",\\\\\\\"scripts/lib/roots.js\\\\\\\",\\\\\\\"scripts/check-protocol-consistency.js\\\\\\\",\\\\\\\"scripts/generate-manifest.js\\\\\\\",\\\\\\\"scripts/skill-graph-drift.js\\\\\\\",\\\\\\\"scripts/skill-overlap.js\\\\\\\",\\\\\\\"scripts/skill-graph-routing-eval.js\\\\\\\",\\\\\\\"docs/manifest-field-mapping.md\\\\\\\"],\\\\\\\"failure_modes\\\\\\\":[\\\\\\\"health_tooling_categories_missing_from_ci\\\\\\\",\\\\\\\"protocol_mapping_drift\\\\\\\",\\\\\\\"eval_thresholds_become_self_attested\\\\\\\",\\\\\\\"overlap_or_drift_checks_not_run_after_batch_changes\\\\\\\"],\\\\\\\"evidence_priority\\\\\\\":\\\\\\\"repo_code_first\\\\\\\"}\",\"portability\":\"{\\\\\\\"readiness\\\\\\\":\\\\\\\"scripted\\\\\\\",\\\\\\\"targets\\\\\\\":[\\\\\\\"skill-md\\\\\\\"]}\",\"lifecycle\":\"{\\\\\\\"stale_after_days\\\\\\\":90,\\\\\\\"review_cadence\\\\\\\":\\\\\\\"quarterly\\\\\\\"}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v5\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/skill-infrastructure/SKILL.md\"}"
9
11
  skill_graph_source_repo: "https://github.com/jacob-balslev/skill-graph"
10
12
  skill_graph_protocol: Skill Metadata Protocol v4
@@ -5,7 +5,9 @@ license: MIT
5
5
  compatibility: "Markdown, YAML, any agent runtime"
6
6
  allowed-tools: Read Grep
7
7
  metadata:
8
- metadata: "{\"schema_version\":6,\"version\":\"1.0.0\",\"type\":\"router\",\"category\":\"agent\",\"domain\":\"agent/skill-system\",\"scope\":\"portable\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-04-18\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-05-13\\\\\\\",\\\\\\\"truth_source_hashes\\\\\\\":{\\\\\\\"scripts/skill-graph-route.js\\\\\\\":\\\\\\\"b9a7b51d0e8b845b11473f479c4593754768c3775508049db7339892b2f08cc2\\\\\\\",\\\\\\\"scripts/skill-graph-routing-eval.js\\\\\\\":\\\\\\\"fffac2858863662bde6bc54c56bb77a219ae93f626e0c8d5886566f998181deb\\\\\\\",\\\\\\\"examples/evals/skill-router.json\\\\\\\":\\\\\\\"fccabcbc5f9d8057536f397fb0fc71a567371f75fb9a21afda343b197af30293\\\\\\\",\\\\\\\"examples/evals/skill-router.routing.json\\\\\\\":\\\\\\\"c4dc88db1e746bea78a7cf96b50c6c84532a07eda42da2e35d790c8928d4da8c\\\\\\\"}}\",\"eval_artifacts\":\"present\",\"eval_state\":\"passing\",\"routing_eval\":\"present\",\"stability\":\"experimental\",\"keywords\":\"[\\\\\\\"skill routing\\\\\\\",\\\\\\\"skill dispatch\\\\\\\",\\\\\\\"keyword routing\\\\\\\",\\\\\\\"route skill\\\\\\\",\\\\\\\"which skill to use\\\\\\\",\\\\\\\"skill selector\\\\\\\",\\\\\\\"routing table\\\\\\\",\\\\\\\"coverage gap\\\\\\\",\\\\\\\"ambiguous skill activation\\\\\\\",\\\\\\\"skill activate\\\\\\\",\\\\\\\"skill activates\\\\\\\",\\\\\\\"activate skill\\\\\\\",\\\\\\\"skill should activate\\\\\\\",\\\\\\\"which skill activates\\\\\\\",\\\\\\\"why did skill activate\\\\\\\",\\\\\\\"why skill activated\\\\\\\",\\\\\\\"routing decision\\\\\\\",\\\\\\\"dispatch request\\\\\\\",\\\\\\\"dispatch agent request\\\\\\\",\\\\\\\"agent request routing\\\\\\\",\\\\\\\"route this request\\\\\\\",\\\\\\\"route the request\\\\\\\",\\\\\\\"find the right skill\\\\\\\",\\\\\\\"choose the right skill\\\\\\\",\\\\\\\"which skill handles\\\\\\\"]\",\"triggers\":\"[\\\\\\\"skill-router\\\\\\\"]\",\"examples\":\"[\\\\\\\"activate the right skill for this agent request: 'my tests are failing in CI'\\\\\\\",\\\\\\\"build a routing table that covers every agent request type we see\\\\\\\",\\\\\\\"why did the documentation skill activate when the user asked about a11y?\\\\\\\",\\\\\\\"find the coverage gaps — which agent requests match no skill at all?\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"audit the graph-audit skill for schema conformance\\\\\\\",\\\\\\\"write a guide explaining how our routing works\\\\\\\",\\\\\\\"reproduce this routing mis-dispatch from production logs\\\\\\\"]\",\"relations\":\"{\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"graph-audit\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"graph-audit verifies ONE skill's metadata; skill-router chooses BETWEEN skills at request time\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"debugging\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"debugging reproduces a specific routing mis-dispatch from evidence; skill-router designs the routing table itself\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"skill-infrastructure\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"skill-infrastructure analyses routing-miss patterns across the whole library to find systemic gaps; skill-router authors the routing logic for one library at a time\\\\\\\"}],\\\\\\\"verify_with\\\\\\\":[\\\\\\\"graph-audit\\\\\\\"]}\",\"grounding\":\"{\\\\\\\"domain_object\\\\\\\":\\\\\\\"Skill Graph reference routing behavior\\\\\\\",\\\\\\\"grounding_mode\\\\\\\":\\\\\\\"repo_specific\\\\\\\",\\\\\\\"truth_sources\\\\\\\":[\\\\\\\"scripts/skill-graph-route.js\\\\\\\",\\\\\\\"scripts/skill-graph-routing-eval.js\\\\\\\",\\\\\\\"examples/evals/skill-router.json\\\\\\\",\\\\\\\"examples/evals/skill-router.routing.json\\\\\\\"],\\\\\\\"failure_modes\\\\\\\":[\\\\\\\"negation_paths_score_as_positive_matches\\\\\\\",\\\\\\\"routing_eval_claim_without_harness_pass\\\\\\\",\\\\\\\"boundary_exclusion_removes_stronger_match\\\\\\\",\\\\\\\"coverage_gap_silently_falls_back\\\\\\\"],\\\\\\\"evidence_priority\\\\\\\":\\\\\\\"repo_code_first\\\\\\\"}\",\"portability\":\"{\\\\\\\"readiness\\\\\\\":\\\\\\\"scripted\\\\\\\",\\\\\\\"targets\\\\\\\":[\\\\\\\"skill-md\\\\\\\"]}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v5\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/skill-router/SKILL.md\"}"
8
+ grounding: "{\"domain_object\":\"Skill Graph reference routing behavior\",\"grounding_mode\":\"repo_specific\",\"truth_sources\":[\"scripts/skill-graph-route.js\",\"scripts/skill-graph-routing-eval.js\",\"examples/evals/skill-router.json\",\"examples/evals/skill-router.routing.json\"],\"failure_modes\":[\"negation_paths_score_as_positive_matches\",\"routing_eval_claim_without_harness_pass\",\"boundary_exclusion_removes_stronger_match\",\"coverage_gap_silently_falls_back\"],\"evidence_priority\":\"repo_code_first\"}"
9
+ drift_check: "{\"last_verified\":\"2026-05-18\",\"truth_source_hashes\":{\"scripts/skill-graph-route.js\":\"7c29b1bc2420887f32809b8f52f5bd448c1234542672e669a68247942dd77df4\",\"scripts/skill-graph-routing-eval.js\":\"fffac2858863662bde6bc54c56bb77a219ae93f626e0c8d5886566f998181deb\",\"examples/evals/skill-router.json\":\"fccabcbc5f9d8057536f397fb0fc71a567371f75fb9a21afda343b197af30293\",\"examples/evals/skill-router.routing.json\":\"c4dc88db1e746bea78a7cf96b50c6c84532a07eda42da2e35d790c8928d4da8c\"}}"
10
+ metadata: "{\"schema_version\":6,\"version\":\"1.0.0\",\"type\":\"router\",\"category\":\"agent\",\"domain\":\"agent/skill-system\",\"scope\":\"portable\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-04-18\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-05-13\\\\\\\",\\\\\\\"truth_source_hashes\\\\\\\":{\\\\\\\"scripts/skill-graph-route.js\\\\\\\":\\\\\\\"b9a7b51d0e8b845b11473f479c4593754768c3775508049db7339892b2f08cc2\\\\\\\",\\\\\\\"scripts/skill-graph-routing-eval.js\\\\\\\":\\\\\\\"fffac2858863662bde6bc54c56bb77a219ae93f626e0c8d5886566f998181deb\\\\\\\",\\\\\\\"examples/evals/skill-router.json\\\\\\\":\\\\\\\"fccabcbc5f9d8057536f397fb0fc71a567371f75fb9a21afda343b197af30293\\\\\\\",\\\\\\\"examples/evals/skill-router.routing.json\\\\\\\":\\\\\\\"c4dc88db1e746bea78a7cf96b50c6c84532a07eda42da2e35d790c8928d4da8c\\\\\\\"}}\",\"eval_artifacts\":\"present\",\"eval_state\":\"passing\",\"routing_eval\":\"present\",\"stability\":\"experimental\",\"keywords\":\"[\\\\\\\"skill routing\\\\\\\",\\\\\\\"skill dispatch\\\\\\\",\\\\\\\"keyword routing\\\\\\\",\\\\\\\"route skill\\\\\\\",\\\\\\\"which skill to use\\\\\\\",\\\\\\\"skill selector\\\\\\\",\\\\\\\"routing table\\\\\\\",\\\\\\\"coverage gap\\\\\\\",\\\\\\\"ambiguous skill activation\\\\\\\",\\\\\\\"skill activate\\\\\\\",\\\\\\\"skill activates\\\\\\\",\\\\\\\"activate skill\\\\\\\",\\\\\\\"skill should activate\\\\\\\",\\\\\\\"which skill activates\\\\\\\",\\\\\\\"why did skill activate\\\\\\\",\\\\\\\"why skill activated\\\\\\\",\\\\\\\"routing decision\\\\\\\",\\\\\\\"dispatch request\\\\\\\",\\\\\\\"dispatch agent request\\\\\\\",\\\\\\\"agent request routing\\\\\\\",\\\\\\\"route this request\\\\\\\",\\\\\\\"route the request\\\\\\\",\\\\\\\"find the right skill\\\\\\\",\\\\\\\"choose the right skill\\\\\\\",\\\\\\\"which skill handles\\\\\\\"]\",\"triggers\":\"[\\\\\\\"skill-router\\\\\\\"]\",\"examples\":\"[\\\\\\\"activate the right skill for this agent request: 'my tests are failing in CI'\\\\\\\",\\\\\\\"build a routing table that covers every agent request type we see\\\\\\\",\\\\\\\"why did the documentation skill activate when the user asked about a11y?\\\\\\\",\\\\\\\"find the coverage gaps — which agent requests match no skill at all?\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"audit the graph-audit skill for schema conformance\\\\\\\",\\\\\\\"write a guide explaining how our routing works\\\\\\\",\\\\\\\"reproduce this routing mis-dispatch from production logs\\\\\\\"]\",\"relations\":\"{\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"graph-audit\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"graph-audit verifies ONE skill's metadata; skill-router chooses BETWEEN skills at request time\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"debugging\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"debugging reproduces a specific routing mis-dispatch from evidence; skill-router designs the routing table itself\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"skill-infrastructure\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"skill-infrastructure analyses routing-miss patterns across the whole library to find systemic gaps; skill-router authors the routing logic for one library at a time\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"middleware-patterns\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"middleware-patterns owns the design of Next.js middleware (request/response transforms, edge runtime, matchers); skill-router owns agent skill dispatch. Writing a guide explaining 'how our routing works' is documentation about Next.js middleware patterns, not an agent skill routing exercise.\\\\\\\"}],\\\\\\\"verify_with\\\\\\\":[\\\\\\\"graph-audit\\\\\\\"]}\",\"grounding\":\"{\\\\\\\"domain_object\\\\\\\":\\\\\\\"Skill Graph reference routing behavior\\\\\\\",\\\\\\\"grounding_mode\\\\\\\":\\\\\\\"repo_specific\\\\\\\",\\\\\\\"truth_sources\\\\\\\":[\\\\\\\"scripts/skill-graph-route.js\\\\\\\",\\\\\\\"scripts/skill-graph-routing-eval.js\\\\\\\",\\\\\\\"examples/evals/skill-router.json\\\\\\\",\\\\\\\"examples/evals/skill-router.routing.json\\\\\\\"],\\\\\\\"failure_modes\\\\\\\":[\\\\\\\"negation_paths_score_as_positive_matches\\\\\\\",\\\\\\\"routing_eval_claim_without_harness_pass\\\\\\\",\\\\\\\"boundary_exclusion_removes_stronger_match\\\\\\\",\\\\\\\"coverage_gap_silently_falls_back\\\\\\\"],\\\\\\\"evidence_priority\\\\\\\":\\\\\\\"repo_code_first\\\\\\\"}\",\"portability\":\"{\\\\\\\"readiness\\\\\\\":\\\\\\\"scripted\\\\\\\",\\\\\\\"targets\\\\\\\":[\\\\\\\"skill-md\\\\\\\"]}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v5\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/skill-router/SKILL.md\"}"
9
11
  skill_graph_source_repo: "https://github.com/jacob-balslev/skill-graph"
10
12
  skill_graph_protocol: Skill Metadata Protocol v4
11
13
  skill_graph_project: Skill Graph
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: "Markdown, Git, any codebase"
6
6
  allowed-tools: Read Grep Bash
7
7
  metadata:
8
- metadata: "{\"schema_version\":6,\"version\":\"1.0.0\",\"type\":\"capability\",\"category\":\"quality\",\"scope\":\"portable\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-04-18\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-04-18\\\\\\\"}\",\"eval_artifacts\":\"present\",\"eval_state\":\"passing\",\"routing_eval\":\"present\",\"stability\":\"experimental\",\"keywords\":\"[\\\\\\\"testing strategy\\\\\\\",\\\\\\\"what to test\\\\\\\",\\\\\\\"what not to test\\\\\\\",\\\\\\\"which test level\\\\\\\",\\\\\\\"test scope\\\\\\\",\\\\\\\"effort vs risk\\\\\\\",\\\\\\\"regression target\\\\\\\",\\\\\\\"failure case coverage\\\\\\\",\\\\\\\"test plan\\\\\\\",\\\\\\\"do I need a test\\\\\\\",\\\\\\\"should I test this\\\\\\\",\\\\\\\"unit or integration\\\\\\\",\\\\\\\"test coverage\\\\\\\",\\\\\\\"pin this behavior\\\\\\\",\\\\\\\"plan test coverage\\\\\\\",\\\\\\\"plan coverage\\\\\\\",\\\\\\\"needs an automated test\\\\\\\",\\\\\\\"automated test\\\\\\\",\\\\\\\"manual QA coverage\\\\\\\",\\\\\\\"passes manual QA\\\\\\\",\\\\\\\"test level decision\\\\\\\"]\",\"triggers\":\"[\\\\\\\"testing-skill\\\\\\\"]\",\"routing_bundles\":\"[\\\\\\\"quality\\\\\\\"]\",\"examples\":\"[\\\\\\\"do I need a unit test for this pure formatter or is integration enough?\\\\\\\",\\\\\\\"what's the right test level for a webhook handler that talks to Stripe?\\\\\\\",\\\\\\\"the feature passes manual QA — does it need an automated test?\\\\\\\",\\\\\\\"pin this regression so the same bug can't slip through again\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"my existing test is failing — why?\\\\\\\",\\\\\\\"write a testing-patterns guide for the contributor docs\\\\\\\",\\\\\\\"clean up this duplicated test setup across three files\\\\\\\"]\",\"relations\":\"{\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"debugging\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"debugging chases a specific observed failure; testing-strategy decides what to test BEFORE a failure exists\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"refactor\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"refactor reshapes code (including test setup) while preserving behavior; testing-strategy decides what coverage to author in the first place\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"integration-test-design\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"integration-test-design owns the design of integration-level tests including their setup and data lifecycle; the 'duplicated test setup across three files' anti_example has token overlap with integration-test setup discipline. testing-strategy decides what level a test should be; integration-test-design owns how to design integration tests once chosen.\\\\\\\"}],\\\\\\\"verify_with\\\\\\\":[\\\\\\\"debugging\\\\\\\"]}\",\"portability\":\"{\\\\\\\"readiness\\\\\\\":\\\\\\\"scripted\\\\\\\",\\\\\\\"targets\\\\\\\":[\\\\\\\"skill-md\\\\\\\"]}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v5\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/testing-strategy/SKILL.md\"}"
8
+ metadata: "{\"schema_version\":6,\"version\":\"1.0.0\",\"type\":\"capability\",\"category\":\"quality\",\"scope\":\"portable\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-04-18\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-04-18\\\\\\\"}\",\"eval_artifacts\":\"present\",\"eval_state\":\"passing\",\"routing_eval\":\"present\",\"stability\":\"experimental\",\"keywords\":\"[\\\\\\\"testing strategy\\\\\\\",\\\\\\\"what to test\\\\\\\",\\\\\\\"what not to test\\\\\\\",\\\\\\\"which test level\\\\\\\",\\\\\\\"test scope\\\\\\\",\\\\\\\"effort vs risk\\\\\\\",\\\\\\\"regression target\\\\\\\",\\\\\\\"failure case coverage\\\\\\\",\\\\\\\"test plan\\\\\\\",\\\\\\\"do I need a test\\\\\\\",\\\\\\\"should I test this\\\\\\\",\\\\\\\"unit or integration\\\\\\\",\\\\\\\"test coverage\\\\\\\",\\\\\\\"pin this behavior\\\\\\\",\\\\\\\"plan test coverage\\\\\\\",\\\\\\\"plan coverage\\\\\\\",\\\\\\\"needs an automated test\\\\\\\",\\\\\\\"automated test\\\\\\\",\\\\\\\"manual QA coverage\\\\\\\",\\\\\\\"passes manual QA\\\\\\\",\\\\\\\"test level decision\\\\\\\",\\\\\\\"right test level\\\\\\\",\\\\\\\"correct test level\\\\\\\",\\\\\\\"test level for webhook\\\\\\\",\\\\\\\"test level for handler\\\\\\\"]\",\"triggers\":\"[\\\\\\\"testing-skill\\\\\\\"]\",\"routing_bundles\":\"[\\\\\\\"quality\\\\\\\"]\",\"examples\":\"[\\\\\\\"do I need a unit test for this pure formatter or is integration enough?\\\\\\\",\\\\\\\"what's the right test level for a webhook handler that talks to Stripe?\\\\\\\",\\\\\\\"the feature passes manual QA — does it need an automated test?\\\\\\\",\\\\\\\"pin this regression so the same bug can't slip through again\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"my existing test is failing — why?\\\\\\\",\\\\\\\"write a testing-patterns guide for the contributor docs\\\\\\\",\\\\\\\"clean up this duplicated test setup across three files\\\\\\\"]\",\"relations\":\"{\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"debugging\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"debugging chases a specific observed failure; testing-strategy decides what to test BEFORE a failure exists\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"refactor\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"refactor reshapes code (including test setup) while preserving behavior; testing-strategy decides what coverage to author in the first place\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"integration-test-design\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"integration-test-design owns the design of integration-level tests including their setup and data lifecycle; the 'duplicated test setup across three files' anti_example has token overlap with integration-test setup discipline. testing-strategy decides what level a test should be; integration-test-design owns how to design integration tests once chosen.\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"microcopy\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"microcopy owns functional UI text and contributor-facing writing; testing-strategy decides what and how to test. Writing a testing-patterns guide for contributor docs is a documentation/writing task owned by microcopy, not a test-scope decision.\\\\\\\"}],\\\\\\\"verify_with\\\\\\\":[\\\\\\\"debugging\\\\\\\"]}\",\"portability\":\"{\\\\\\\"readiness\\\\\\\":\\\\\\\"scripted\\\\\\\",\\\\\\\"targets\\\\\\\":[\\\\\\\"skill-md\\\\\\\"]}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v5\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/testing-strategy/SKILL.md\"}"
9
9
  skill_graph_source_repo: "https://github.com/jacob-balslev/skill-graph"
10
10
  skill_graph_protocol: Skill Metadata Protocol v4
11
11
  skill_graph_project: Skill Graph
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skill-graph/cli",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "Skill Graph — library-level tooling for SKILL.md libraries: lint, manifest compiler, router, drift sentinel, and export pipeline.",
5
5
  "type": "commonjs",
6
6
  "license": "Apache-2.0",
@@ -59,6 +59,8 @@
59
59
  "marketplace:verify": "node scripts/export-marketplace-skills.js --check && node scripts/verify-skill-md-export.js --plain marketplace/skills",
60
60
  "overlap": "node scripts/skill-overlap.js",
61
61
  "drift": "node scripts/skill-graph-drift.js",
62
+ "status": "node scripts/build-status-doc.js",
63
+ "status:check": "node scripts/build-status-doc.js --check",
62
64
  "test:unit": "node scripts/__tests__/test-v3-1-skos-runtime.js && node scripts/__tests__/test-router-paths.js && node scripts/__tests__/test-v3-1-alias-contract.js && node scripts/__tests__/test-export-parser-drift.js && node scripts/__tests__/test-marketplace-export.js && node scripts/__tests__/test-stability-promotion.js && node bin/skill-graph.js --help",
63
65
  "test:migrate": "node scripts/__tests__/migrate-skill-v3-to-v4.test.js && node scripts/__tests__/migrate-skill-v2-to-v3.test.js",
64
66
  "test": "npm run test:unit && npm run test:migrate",
@@ -167,6 +167,10 @@
167
167
  "category": {
168
168
  "type": "string"
169
169
  },
170
+ "secondary_categories": {
171
+ "type": "array",
172
+ "items": { "type": "string" }
173
+ },
170
174
  "domain": {
171
175
  "type": "string"
172
176
  },
@@ -190,6 +194,10 @@
190
194
  "deprecated"
191
195
  ]
192
196
  },
197
+ "marketplace_tier": {
198
+ "type": "string",
199
+ "enum": ["S", "A", "B", "C"]
200
+ },
193
201
  "superseded_by": {
194
202
  "type": "string",
195
203
  "description": "Name of the skill that replaces this one. Populated when authored `stability: deprecated`. Consumers use this to follow deprecation chains automatically."
@@ -167,6 +167,10 @@
167
167
  "category": {
168
168
  "type": "string"
169
169
  },
170
+ "secondary_categories": {
171
+ "type": "array",
172
+ "items": { "type": "string" }
173
+ },
170
174
  "domain": {
171
175
  "type": "string"
172
176
  },
@@ -190,6 +194,10 @@
190
194
  "deprecated"
191
195
  ]
192
196
  },
197
+ "marketplace_tier": {
198
+ "type": "string",
199
+ "enum": ["S", "A", "B", "C"]
200
+ },
193
201
  "superseded_by": {
194
202
  "type": "string",
195
203
  "description": "Name of the skill that replaces this one. Populated when authored `stability: deprecated`. Consumers use this to follow deprecation chains automatically."
@@ -45,6 +45,7 @@
45
45
  "@type": "xsd:date"
46
46
  },
47
47
  "stability": "sg:stability",
48
+ "marketplace_tier": "sg:marketplaceTier",
48
49
  "superseded_by": {
49
50
  "@id": "prov:wasRevisionOf",
50
51
  "@type": "@id",
@@ -54,6 +55,10 @@
54
55
  "archetype": "sg:archetype",
55
56
  "scope": "sg:scope",
56
57
  "category": "sg:browseCategory",
58
+ "secondary_categories": {
59
+ "@id": "sg:secondaryCategories",
60
+ "@container": "@set"
61
+ },
57
62
  "keywords": {
58
63
  "@id": "dcat:keyword",
59
64
  "@container": "@set"
@@ -89,6 +89,23 @@
89
89
  "pattern": "^[a-z0-9][a-z0-9-]*(/[a-z0-9][a-z0-9-]*)*$",
90
90
  "description": "Hierarchical domain path using slash-delimited segments (e.g., `ecommerce/integrations/shopify`). Complements `category`; the flat browse shelf and the domain tree answer different questions."
91
91
  },
92
+ "secondary_categories": {
93
+ "type": "array",
94
+ "maxItems": 2,
95
+ "uniqueItems": true,
96
+ "items": {
97
+ "type": "string",
98
+ "enum": [
99
+ "foundations",
100
+ "engineering",
101
+ "design",
102
+ "quality",
103
+ "agent",
104
+ "product"
105
+ ]
106
+ },
107
+ "description": "Additive tags for cross-listing in marketplace collections. Primary `category` is MECE and decides folder placement; `secondary_categories` lets a skill that genuinely serves two audiences (e.g., `playwright-cli` is primarily `quality` but also relevant to `engineering`) appear in additional marketplace collections without affecting filesystem layout. Max 2 entries to prevent dilution. Drawn from the same closed 6-enum as `category`; MUST NOT include the primary `category` value."
108
+ },
92
109
  "scope": {
93
110
  "type": "string",
94
111
  "enum": [
@@ -386,6 +403,16 @@
386
403
  ],
387
404
  "description": "Lifecycle posture for consumers. `experimental` (subject to change), `stable` (production-ready), `frozen` (no further changes expected), `deprecated` (use `superseded_by` to name the replacement). Drives consumer pinning decisions."
388
405
  },
406
+ "marketplace_tier": {
407
+ "type": "string",
408
+ "enum": [
409
+ "S",
410
+ "A",
411
+ "B",
412
+ "C"
413
+ ],
414
+ "description": "Publication priority for the public marketplace at `github.com/jacob-balslev/skills` / `skills.sh`. `S` = featured (top-of-README, individual hero copy). `A` = high-demand (named in collection tables). `B` = standard utility (included in collection tables). `C` = niche (collapsed 'More' section). Omit entirely for skills that should not be published. Sourced from `marketplace-publication-priority-*.md` and authored per skill. Lint validates the enum; consumers (export-marketplace-skills.js, generate-marketplace-readmes.js) filter and group on this field."
415
+ },
389
416
  "superseded_by": {
390
417
  "type": "string",
391
418
  "description": "Name of the skill that replaces this one. Required when `stability: deprecated` — enforced by the allOf rule so every deprecated skill names its successor and consumers can follow the chain automatically. Omit on non-deprecated skills."
@@ -82,9 +82,13 @@ const exportedA11yFm = parseFrontmatter(exportedA11y);
82
82
  assert(exportedA11yFm, 'marketplace export should have frontmatter');
83
83
  const shape = validateExportedFrontmatter(exportedA11yFm);
84
84
  assert(shape.errors.length === 0, `marketplace export should be plain SKILL.md shape: ${shape.errors.join('; ')}`);
85
+ // After the M1 category restructure, a11y lives at skills/quality/a11y/SKILL.md
86
+ // in the sibling skills repo. Derive the expected path from the resolved source
87
+ // rather than hardcoding it, so the assertion stays correct if the skill moves again.
88
+ const _expectedCanonicalSkill = a11y.canonicalSkillPath;
85
89
  assert(
86
- exportedA11yFm.metadata.skill_graph_canonical_skill === 'skills/a11y/SKILL.md',
87
- 'marketplace export should preserve canonical source path'
90
+ exportedA11yFm.metadata.skill_graph_canonical_skill === _expectedCanonicalSkill,
91
+ `marketplace export should preserve canonical source path (expected: ${_expectedCanonicalSkill})`
88
92
  );
89
93
  assert(
90
94
  exportedA11yFm.metadata.skill_graph_protocol === SKILL_GRAPH_PROTOCOL &&
@@ -56,8 +56,8 @@ const fm = {
56
56
  'allowed-tools': 'Read Grep',
57
57
  allowed_tools: 'Read Grep',
58
58
  compatibility: {
59
- runtimes: ['claude-code>=2.0'],
60
- agent_runtimes: ['claude-code>=2.0'],
59
+ runtimes: ['agent-runtime>=2.0'],
60
+ agent_runtimes: ['agent-runtime>=2.0'],
61
61
  node: '>=20',
62
62
  node_version: '>=20',
63
63
  },
@@ -87,7 +87,7 @@ assert(entry.domain === fm.domain, 'domain should pass through');
87
87
  assert(entry.allowed_tools === fm.allowed_tools, 'allowed_tools alias should pass through');
88
88
  assert(entry.health.reviewed_at === fm.reviewed_at, 'reviewed_at alias should project to health.reviewed_at');
89
89
  assert(entry.health.eval && entry.health.eval.content_state === 'unverified', 'nested eval alias should project to health.eval');
90
- assert(entry.compatibility.agent_runtimes[0] === 'claude-code>=2.0', 'compatibility.agent_runtimes should pass through');
90
+ assert(entry.compatibility.agent_runtimes[0] === 'agent-runtime>=2.0', 'compatibility.agent_runtimes should pass through');
91
91
  assert(entry.compatibility.node_version === '>=20', 'compatibility.node_version should pass through');
92
92
  assert(entry.grounding.subject === 'Alias contract', 'grounding.subject should pass through');
93
93
  assert(entry.grounding.claim_scope === 'repo_specific', 'grounding.claim_scope should pass through');