agentscamp 0.5.0 → 0.6.0
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.
- package/README.md +2 -2
- package/content/manifest.json +212 -2
- package/content/skills/circular-dependency-breaker.md +48 -0
- package/content/skills/commit-splitter.md +54 -0
- package/content/skills/dashboard-designer.md +38 -0
- package/content/skills/deadlock-diagnoser.md +45 -0
- package/content/skills/feature-flag-retirer.md +44 -0
- package/content/skills/flamegraph-analyzer.md +35 -0
- package/content/skills/git-blame-investigator.md +34 -0
- package/content/skills/graphql-schema-designer.md +49 -0
- package/content/skills/hallucination-evaluator.md +40 -0
- package/content/skills/integration-test-designer.md +81 -0
- package/content/skills/model-router-designer.md +39 -0
- package/content/skills/onboarding-guide-writer.md +84 -0
- package/content/skills/rbac-designer.md +82 -0
- package/content/skills/release-notes-writer.md +78 -0
- package/content/skills/web-vitals-optimizer.md +34 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# agentscamp
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 198 ready-to-use Claude Code agents, skills, and slash commands — installable in one command.
|
|
4
4
|
|
|
5
5
|
[AgentsCamp](https://agentscamp.com) is a curated, format-validated directory of AI coding artifacts. This CLI bundles the full catalog and installs items straight into your `.claude/` directory.
|
|
6
6
|
|
|
@@ -43,7 +43,7 @@ These are Claude Code's standard locations — agents get delegated to automatic
|
|
|
43
43
|
## What's inside
|
|
44
44
|
|
|
45
45
|
- **58 agents** — specialized subagents for development, data/AI, infra, security, and more → [browse agents](https://agentscamp.com/agents)
|
|
46
|
-
- **
|
|
46
|
+
- **90 skills** — on-demand capabilities for testing, databases, refactoring, releases → [browse skills](https://agentscamp.com/skills)
|
|
47
47
|
- **50 commands** — reusable slash commands for planning, review, git, scaffolding → [browse commands](https://agentscamp.com/commands)
|
|
48
48
|
|
|
49
49
|
Every item has a full page with docs, examples, and related picks at [agentscamp.com](https://agentscamp.com).
|
package/content/manifest.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-06-18T02:
|
|
3
|
+
"generatedAt": "2026-06-18T02:50:33.985Z",
|
|
4
4
|
"counts": {
|
|
5
5
|
"agents": 58,
|
|
6
|
-
"skills":
|
|
6
|
+
"skills": 90,
|
|
7
7
|
"commands": 50
|
|
8
8
|
},
|
|
9
9
|
"items": [
|
|
@@ -1762,6 +1762,20 @@
|
|
|
1762
1762
|
"installAs": "skills/chunking-strategy-optimizer/SKILL.md",
|
|
1763
1763
|
"url": "https://agentscamp.com/skills/data/chunking-strategy-optimizer"
|
|
1764
1764
|
},
|
|
1765
|
+
{
|
|
1766
|
+
"id": "skills/circular-dependency-breaker",
|
|
1767
|
+
"type": "skill",
|
|
1768
|
+
"slug": "circular-dependency-breaker",
|
|
1769
|
+
"category": "refactor",
|
|
1770
|
+
"title": "Circular Dependency Breaker",
|
|
1771
|
+
"description": "Detect and break a circular import — map the exact cycle with a real tool, then break the right edge by extracting the shared piece into a leaf module, inverting a layering dependency, merging two falsely-split modules, or (last resort) deferring an import. Use when you hit an import cycle error, an undefined-on-import or 'cannot access before initialization' bug, or a bundler/linter flags a cycle.",
|
|
1772
|
+
"topics": [
|
|
1773
|
+
"coding-languages"
|
|
1774
|
+
],
|
|
1775
|
+
"file": "skills/circular-dependency-breaker.md",
|
|
1776
|
+
"installAs": "skills/circular-dependency-breaker/SKILL.md",
|
|
1777
|
+
"url": "https://agentscamp.com/skills/refactor/circular-dependency-breaker"
|
|
1778
|
+
},
|
|
1765
1779
|
{
|
|
1766
1780
|
"id": "skills/claude-settings-auditor",
|
|
1767
1781
|
"type": "skill",
|
|
@@ -1790,6 +1804,20 @@
|
|
|
1790
1804
|
"installAs": "skills/cold-start-optimizer/SKILL.md",
|
|
1791
1805
|
"url": "https://agentscamp.com/skills/performance/cold-start-optimizer"
|
|
1792
1806
|
},
|
|
1807
|
+
{
|
|
1808
|
+
"id": "skills/commit-splitter",
|
|
1809
|
+
"type": "skill",
|
|
1810
|
+
"slug": "commit-splitter",
|
|
1811
|
+
"category": "git",
|
|
1812
|
+
"title": "Commit Splitter",
|
|
1813
|
+
"description": "Split one big, mixed-up change into a series of small, atomic commits — each a single logical change that builds and passes tests on its own — by grouping hunks by intent and staging them piecemeal. Use when a working tree or a fat commit mixes a feature, a refactor, a bug fix, and formatting, or before opening a PR you want reviewers to actually read.",
|
|
1814
|
+
"topics": [
|
|
1815
|
+
"review-qa"
|
|
1816
|
+
],
|
|
1817
|
+
"file": "skills/commit-splitter.md",
|
|
1818
|
+
"installAs": "skills/commit-splitter/SKILL.md",
|
|
1819
|
+
"url": "https://agentscamp.com/skills/git/commit-splitter"
|
|
1820
|
+
},
|
|
1793
1821
|
{
|
|
1794
1822
|
"id": "skills/connection-pool-tuner",
|
|
1795
1823
|
"type": "skill",
|
|
@@ -1846,6 +1874,20 @@
|
|
|
1846
1874
|
"installAs": "skills/coverage-gap-finder/SKILL.md",
|
|
1847
1875
|
"url": "https://agentscamp.com/skills/testing/coverage-gap-finder"
|
|
1848
1876
|
},
|
|
1877
|
+
{
|
|
1878
|
+
"id": "skills/dashboard-designer",
|
|
1879
|
+
"type": "skill",
|
|
1880
|
+
"slug": "dashboard-designer",
|
|
1881
|
+
"category": "observability",
|
|
1882
|
+
"title": "Dashboard Designer",
|
|
1883
|
+
"description": "Design a service dashboard that answers one question at a glance — is the service healthy, and if not, where's the problem? — by structuring panels around RED/USE instead of dumping every metric. Use when a service has no dashboard, when the existing one is an unreadable metric wall, or during incident-readiness prep.",
|
|
1884
|
+
"topics": [
|
|
1885
|
+
"devops-infra"
|
|
1886
|
+
],
|
|
1887
|
+
"file": "skills/dashboard-designer.md",
|
|
1888
|
+
"installAs": "skills/dashboard-designer/SKILL.md",
|
|
1889
|
+
"url": "https://agentscamp.com/skills/observability/dashboard-designer"
|
|
1890
|
+
},
|
|
1849
1891
|
{
|
|
1850
1892
|
"id": "skills/dead-code-finder",
|
|
1851
1893
|
"type": "skill",
|
|
@@ -1860,6 +1902,20 @@
|
|
|
1860
1902
|
"installAs": "skills/dead-code-finder/SKILL.md",
|
|
1861
1903
|
"url": "https://agentscamp.com/skills/refactor/dead-code-finder"
|
|
1862
1904
|
},
|
|
1905
|
+
{
|
|
1906
|
+
"id": "skills/deadlock-diagnoser",
|
|
1907
|
+
"type": "skill",
|
|
1908
|
+
"slug": "deadlock-diagnoser",
|
|
1909
|
+
"category": "database",
|
|
1910
|
+
"title": "Deadlock Diagnoser",
|
|
1911
|
+
"description": "Diagnose a database deadlock from the engine's own deadlock report, reconstruct the lock cycle (A holds 1 wants 2, B holds 2 wants 1), name the root cause — almost always two code paths locking the same rows in different orders — and fix it with consistent lock ordering, shorter transactions, and a retry-the-victim safeguard. Use when the DB logs deadlock errors, when transactions intermittently fail under load, or when queries mysteriously block each other.",
|
|
1912
|
+
"topics": [
|
|
1913
|
+
"devops-infra"
|
|
1914
|
+
],
|
|
1915
|
+
"file": "skills/deadlock-diagnoser.md",
|
|
1916
|
+
"installAs": "skills/deadlock-diagnoser/SKILL.md",
|
|
1917
|
+
"url": "https://agentscamp.com/skills/database/deadlock-diagnoser"
|
|
1918
|
+
},
|
|
1863
1919
|
{
|
|
1864
1920
|
"id": "skills/dependency-audit",
|
|
1865
1921
|
"type": "skill",
|
|
@@ -1960,6 +2016,20 @@
|
|
|
1960
2016
|
"installAs": "skills/extract-module/SKILL.md",
|
|
1961
2017
|
"url": "https://agentscamp.com/skills/refactor/extract-module"
|
|
1962
2018
|
},
|
|
2019
|
+
{
|
|
2020
|
+
"id": "skills/feature-flag-retirer",
|
|
2021
|
+
"type": "skill",
|
|
2022
|
+
"slug": "feature-flag-retirer",
|
|
2023
|
+
"category": "refactor",
|
|
2024
|
+
"title": "Feature Flag Retirer",
|
|
2025
|
+
"description": "Retire stale feature flags by confirming each flag's decided final state, then collapsing every conditional to the winning branch and deleting the loser plus the now-dead code it reached. Use when temporary flags have outlived their rollout, when flag conditionals clutter the code, or during a flag-debt cleanup.",
|
|
2026
|
+
"topics": [
|
|
2027
|
+
"coding-languages"
|
|
2028
|
+
],
|
|
2029
|
+
"file": "skills/feature-flag-retirer.md",
|
|
2030
|
+
"installAs": "skills/feature-flag-retirer/SKILL.md",
|
|
2031
|
+
"url": "https://agentscamp.com/skills/refactor/feature-flag-retirer"
|
|
2032
|
+
},
|
|
1963
2033
|
{
|
|
1964
2034
|
"id": "skills/finetune-dataset-builder",
|
|
1965
2035
|
"type": "skill",
|
|
@@ -1974,6 +2044,34 @@
|
|
|
1974
2044
|
"installAs": "skills/finetune-dataset-builder/SKILL.md",
|
|
1975
2045
|
"url": "https://agentscamp.com/skills/data/finetune-dataset-builder"
|
|
1976
2046
|
},
|
|
2047
|
+
{
|
|
2048
|
+
"id": "skills/flamegraph-analyzer",
|
|
2049
|
+
"type": "skill",
|
|
2050
|
+
"slug": "flamegraph-analyzer",
|
|
2051
|
+
"category": "performance",
|
|
2052
|
+
"title": "Flamegraph Analyzer",
|
|
2053
|
+
"description": "Turn a CPU profile or flamegraph into a concrete optimization instead of guessing where the time goes: capture under a realistic workload with a sampling profiler, read the graph correctly (width = time, depth ≠ time), find the widest self-time leaves, ask if that work is necessary/redundant/algorithmically wrong, fix the biggest contributor, then re-profile. Use when code is CPU-bound and slow, a function is hot but you don't know which part, or you have a profile you can't interpret.",
|
|
2054
|
+
"topics": [
|
|
2055
|
+
"review-qa"
|
|
2056
|
+
],
|
|
2057
|
+
"file": "skills/flamegraph-analyzer.md",
|
|
2058
|
+
"installAs": "skills/flamegraph-analyzer/SKILL.md",
|
|
2059
|
+
"url": "https://agentscamp.com/skills/performance/flamegraph-analyzer"
|
|
2060
|
+
},
|
|
2061
|
+
{
|
|
2062
|
+
"id": "skills/git-blame-investigator",
|
|
2063
|
+
"type": "skill",
|
|
2064
|
+
"slug": "git-blame-investigator",
|
|
2065
|
+
"category": "git",
|
|
2066
|
+
"title": "Git Blame Investigator",
|
|
2067
|
+
"description": "Reconstruct why a line of code exists from Git history — find the originating commit, read its message and full diff for intent, and see through reformatting/rename commits with ignore-revs and the pickaxe — before you change or delete it. Use when a line looks wrong or pointless and you want to remove it, when tracing a regression to its commit, or when onboarding to unfamiliar code.",
|
|
2068
|
+
"topics": [
|
|
2069
|
+
"review-qa"
|
|
2070
|
+
],
|
|
2071
|
+
"file": "skills/git-blame-investigator.md",
|
|
2072
|
+
"installAs": "skills/git-blame-investigator/SKILL.md",
|
|
2073
|
+
"url": "https://agentscamp.com/skills/git/git-blame-investigator"
|
|
2074
|
+
},
|
|
1977
2075
|
{
|
|
1978
2076
|
"id": "skills/github-actions-optimizer",
|
|
1979
2077
|
"type": "skill",
|
|
@@ -1988,6 +2086,20 @@
|
|
|
1988
2086
|
"installAs": "skills/github-actions-optimizer/SKILL.md",
|
|
1989
2087
|
"url": "https://agentscamp.com/skills/workflow/github-actions-optimizer"
|
|
1990
2088
|
},
|
|
2089
|
+
{
|
|
2090
|
+
"id": "skills/graphql-schema-designer",
|
|
2091
|
+
"type": "skill",
|
|
2092
|
+
"slug": "graphql-schema-designer",
|
|
2093
|
+
"category": "api",
|
|
2094
|
+
"title": "GraphQL Schema Designer",
|
|
2095
|
+
"description": "Design a clean, evolvable GraphQL schema (SDL) that won't paint you into a corner — model the graph around domain types and their relationships rather than as RPC-over-GraphQL, set nullability deliberately, standardize lists with Relay connections, plan DataLoader batching for per-parent fields, and evolve by adding + @deprecated instead of versioning. Use when designing a new GraphQL API, reviewing an SDL, or migrating REST endpoints to a graph.",
|
|
2096
|
+
"topics": [
|
|
2097
|
+
"architecture"
|
|
2098
|
+
],
|
|
2099
|
+
"file": "skills/graphql-schema-designer.md",
|
|
2100
|
+
"installAs": "skills/graphql-schema-designer/SKILL.md",
|
|
2101
|
+
"url": "https://agentscamp.com/skills/api/graphql-schema-designer"
|
|
2102
|
+
},
|
|
1991
2103
|
{
|
|
1992
2104
|
"id": "skills/graphrag-scaffolder",
|
|
1993
2105
|
"type": "skill",
|
|
@@ -2002,6 +2114,20 @@
|
|
|
2002
2114
|
"installAs": "skills/graphrag-scaffolder/SKILL.md",
|
|
2003
2115
|
"url": "https://agentscamp.com/skills/data/graphrag-scaffolder"
|
|
2004
2116
|
},
|
|
2117
|
+
{
|
|
2118
|
+
"id": "skills/hallucination-evaluator",
|
|
2119
|
+
"type": "skill",
|
|
2120
|
+
"slug": "hallucination-evaluator",
|
|
2121
|
+
"category": "data",
|
|
2122
|
+
"title": "Hallucination Evaluator",
|
|
2123
|
+
"description": "Detect and measure ungroundedness in LLM and RAG outputs — claims the source doesn't support — by decomposing answers into atomic claims and checking each for entailment, so you can quantify faithfulness and gate on it instead of eyeballing it. Use when a RAG/LLM feature makes confident wrong claims, before shipping anything that must be factual, or to add a groundedness gate to evals/CI.",
|
|
2124
|
+
"topics": [
|
|
2125
|
+
"llm-evals"
|
|
2126
|
+
],
|
|
2127
|
+
"file": "skills/hallucination-evaluator.md",
|
|
2128
|
+
"installAs": "skills/hallucination-evaluator/SKILL.md",
|
|
2129
|
+
"url": "https://agentscamp.com/skills/data/hallucination-evaluator"
|
|
2130
|
+
},
|
|
2005
2131
|
{
|
|
2006
2132
|
"id": "skills/hook-writer",
|
|
2007
2133
|
"type": "skill",
|
|
@@ -2044,6 +2170,20 @@
|
|
|
2044
2170
|
"installAs": "skills/idempotency-designer/SKILL.md",
|
|
2045
2171
|
"url": "https://agentscamp.com/skills/api/idempotency-designer"
|
|
2046
2172
|
},
|
|
2173
|
+
{
|
|
2174
|
+
"id": "skills/integration-test-designer",
|
|
2175
|
+
"type": "skill",
|
|
2176
|
+
"slug": "integration-test-designer",
|
|
2177
|
+
"category": "testing",
|
|
2178
|
+
"title": "Integration Test Designer",
|
|
2179
|
+
"description": "Design integration tests that exercise components against REAL collaborators — actual database, queue, HTTP boundary — at a deliberately chosen seam, instead of a unit suite that mocks everything or a slow flaky full E2E. Use when bugs slip past green unit tests, when wiring or contracts between layers break in production, or when a mocked DB test passes but the real query/migration/serialization fails.",
|
|
2180
|
+
"topics": [
|
|
2181
|
+
"review-qa"
|
|
2182
|
+
],
|
|
2183
|
+
"file": "skills/integration-test-designer.md",
|
|
2184
|
+
"installAs": "skills/integration-test-designer/SKILL.md",
|
|
2185
|
+
"url": "https://agentscamp.com/skills/testing/integration-test-designer"
|
|
2186
|
+
},
|
|
2047
2187
|
{
|
|
2048
2188
|
"id": "skills/llm-as-judge-scorer",
|
|
2049
2189
|
"type": "skill",
|
|
@@ -2171,6 +2311,20 @@
|
|
|
2171
2311
|
"installAs": "skills/mock-data-factory/SKILL.md",
|
|
2172
2312
|
"url": "https://agentscamp.com/skills/testing/mock-data-factory"
|
|
2173
2313
|
},
|
|
2314
|
+
{
|
|
2315
|
+
"id": "skills/model-router-designer",
|
|
2316
|
+
"type": "skill",
|
|
2317
|
+
"slug": "model-router-designer",
|
|
2318
|
+
"category": "data",
|
|
2319
|
+
"title": "Model Router Designer",
|
|
2320
|
+
"description": "Design a model router that sends each LLM request to the cheapest model that can handle it and escalates only the hard cases to the strongest — cutting cost and latency without tanking quality, gated by an eval set so the savings don't come from silently worse answers. Use when one expensive model serves all traffic (most of it easy), when LLM cost or latency is too high, or when balancing quality against spend across a range of request difficulty.",
|
|
2321
|
+
"topics": [
|
|
2322
|
+
"llm-app-dev"
|
|
2323
|
+
],
|
|
2324
|
+
"file": "skills/model-router-designer.md",
|
|
2325
|
+
"installAs": "skills/model-router-designer/SKILL.md",
|
|
2326
|
+
"url": "https://agentscamp.com/skills/data/model-router-designer"
|
|
2327
|
+
},
|
|
2174
2328
|
{
|
|
2175
2329
|
"id": "skills/multimodal-document-extractor",
|
|
2176
2330
|
"type": "skill",
|
|
@@ -2200,6 +2354,20 @@
|
|
|
2200
2354
|
"installAs": "skills/mutation-test-runner/SKILL.md",
|
|
2201
2355
|
"url": "https://agentscamp.com/skills/testing/mutation-test-runner"
|
|
2202
2356
|
},
|
|
2357
|
+
{
|
|
2358
|
+
"id": "skills/onboarding-guide-writer",
|
|
2359
|
+
"type": "skill",
|
|
2360
|
+
"slug": "onboarding-guide-writer",
|
|
2361
|
+
"category": "docs",
|
|
2362
|
+
"title": "Onboarding Guide Writer",
|
|
2363
|
+
"description": "Write a developer onboarding guide that gets a new contributor from clone to first merged change fast — a verified golden path, a quick architecture map, the real workflow conventions, and the gotchas that live only in senior engineers' heads. Use when a repo has no onboarding doc, when new hires keep asking the same setup questions, or when the README is a marketing page instead of a contributor guide.",
|
|
2364
|
+
"topics": [
|
|
2365
|
+
"workflow-prompting"
|
|
2366
|
+
],
|
|
2367
|
+
"file": "skills/onboarding-guide-writer.md",
|
|
2368
|
+
"installAs": "skills/onboarding-guide-writer/SKILL.md",
|
|
2369
|
+
"url": "https://agentscamp.com/skills/docs/onboarding-guide-writer"
|
|
2370
|
+
},
|
|
2203
2371
|
{
|
|
2204
2372
|
"id": "skills/openapi-doc-writer",
|
|
2205
2373
|
"type": "skill",
|
|
@@ -2398,6 +2566,20 @@
|
|
|
2398
2566
|
"installAs": "skills/rate-limiter-designer/SKILL.md",
|
|
2399
2567
|
"url": "https://agentscamp.com/skills/api/rate-limiter-designer"
|
|
2400
2568
|
},
|
|
2569
|
+
{
|
|
2570
|
+
"id": "skills/rbac-designer",
|
|
2571
|
+
"type": "skill",
|
|
2572
|
+
"slug": "rbac-designer",
|
|
2573
|
+
"category": "security",
|
|
2574
|
+
"title": "RBAC Designer",
|
|
2575
|
+
"description": "Design the authorization model itself — fine-grained permissions on resources composed into roles, with the right amount of resource/tenant scoping — instead of scattering role-name checks through handlers. Use when building multi-user or multi-tenant authorization, when `if user.isAdmin` checks are sprawling across the codebase, or when 'who can do what' needs a real model rather than ad-hoc gates.",
|
|
2576
|
+
"topics": [
|
|
2577
|
+
"architecture"
|
|
2578
|
+
],
|
|
2579
|
+
"file": "skills/rbac-designer.md",
|
|
2580
|
+
"installAs": "skills/rbac-designer/SKILL.md",
|
|
2581
|
+
"url": "https://agentscamp.com/skills/security/rbac-designer"
|
|
2582
|
+
},
|
|
2401
2583
|
{
|
|
2402
2584
|
"id": "skills/react-render-profiler",
|
|
2403
2585
|
"type": "skill",
|
|
@@ -2427,6 +2609,20 @@
|
|
|
2427
2609
|
"installAs": "skills/readme-generator/SKILL.md",
|
|
2428
2610
|
"url": "https://agentscamp.com/skills/docs/readme-generator"
|
|
2429
2611
|
},
|
|
2612
|
+
{
|
|
2613
|
+
"id": "skills/release-notes-writer",
|
|
2614
|
+
"type": "skill",
|
|
2615
|
+
"slug": "release-notes-writer",
|
|
2616
|
+
"category": "release",
|
|
2617
|
+
"title": "Release Notes Writer",
|
|
2618
|
+
"description": "Write user-facing release notes — the curated 'what's new and what it means for you' — by starting from the real changes (git log / merged PRs / the changelog since the last release) and translating developer-speak into user impact, grouped by what the user cares about with breaking changes and required actions surfaced first. Use when shipping a release to users or customers and the raw commit log isn't something a user should read, when you need a published GitHub-release / blog / in-app announcement, or when a breaking change must be made unmissable so upgrades don't break.",
|
|
2619
|
+
"topics": [
|
|
2620
|
+
"devops-infra"
|
|
2621
|
+
],
|
|
2622
|
+
"file": "skills/release-notes-writer.md",
|
|
2623
|
+
"installAs": "skills/release-notes-writer/SKILL.md",
|
|
2624
|
+
"url": "https://agentscamp.com/skills/release/release-notes-writer"
|
|
2625
|
+
},
|
|
2430
2626
|
{
|
|
2431
2627
|
"id": "skills/runbook-writer",
|
|
2432
2628
|
"type": "skill",
|
|
@@ -2651,6 +2847,20 @@
|
|
|
2651
2847
|
"installAs": "skills/web-research-pipeline/SKILL.md",
|
|
2652
2848
|
"url": "https://agentscamp.com/skills/data/web-research-pipeline"
|
|
2653
2849
|
},
|
|
2850
|
+
{
|
|
2851
|
+
"id": "skills/web-vitals-optimizer",
|
|
2852
|
+
"type": "skill",
|
|
2853
|
+
"slug": "web-vitals-optimizer",
|
|
2854
|
+
"category": "performance",
|
|
2855
|
+
"title": "Web Vitals Optimizer",
|
|
2856
|
+
"description": "Diagnose and fix Core Web Vitals — LCP, CLS, and INP — by treating real-user field data at p75 as the source of truth, using Lighthouse/WebPageTest only to find the at-fault element, script, or shift, then applying the one targeted fix per metric and re-measuring. Use when a page feels slow, scores poorly on PageSpeed/Lighthouse, or fails CWV in CrUX/RUM field data.",
|
|
2857
|
+
"topics": [
|
|
2858
|
+
"coding-languages"
|
|
2859
|
+
],
|
|
2860
|
+
"file": "skills/web-vitals-optimizer.md",
|
|
2861
|
+
"installAs": "skills/web-vitals-optimizer/SKILL.md",
|
|
2862
|
+
"url": "https://agentscamp.com/skills/performance/web-vitals-optimizer"
|
|
2863
|
+
},
|
|
2654
2864
|
{
|
|
2655
2865
|
"id": "skills/webhook-handler-scaffolder",
|
|
2656
2866
|
"type": "skill",
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "circular-dependency-breaker"
|
|
3
|
+
description: "Detect and break a circular import — map the exact cycle with a real tool, then break the right edge by extracting the shared piece into a leaf module, inverting a layering dependency, merging two falsely-split modules, or (last resort) deferring an import. Use when you hit an import cycle error, an undefined-on-import or 'cannot access before initialization' bug, or a bundler/linter flags a cycle."
|
|
4
|
+
allowed-tools: "Read, Grep, Glob, Edit"
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A circular import is two or more modules that need each other to finish loading before either can finish loading — so one of them gets a half-built version of the other, and you get an `undefined` export, a `cannot access X before initialization`, or a bundler warning that surfaces "randomly" depending on which file ran first. This skill refuses to guess: it maps the exact cycle with a real dependency tool, identifies *which edge* is the wrong one, breaks it with the technique that matches the cause, and re-runs the tool to prove the cycle is gone.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- An import throws `cannot access '<x>' before initialization`, `ReferenceError`, or an export reads as `undefined` even though it is clearly exported.
|
|
13
|
+
- A bundler (webpack/Vite/Rollup/esbuild), a linter (`import/no-cycle`), `madge --circular`, `import-linter`, or `go vet` flags a circular dependency.
|
|
14
|
+
- A value works in one entry order and breaks in another — tests pass alone but fail in a suite, or prod breaks while dev works, because module load order differs.
|
|
15
|
+
- You are about to "fix" a crash by moving an import inside a function and want to know whether that hides the real problem (it does).
|
|
16
|
+
|
|
17
|
+
## Instructions
|
|
18
|
+
|
|
19
|
+
1. **Map the cycle with a tool before changing one line.** Do not infer the cycle from the stack trace — the trace shows where it *crashed*, not which edge to cut. Run the right tool for the stack: JS/TS `npx madge --circular --extensions ts,tsx src` or `npx dpdm --circular src/index.ts`; Python `import-linter` (with a `[importlinter]` contract) or `pydeps --show-cycles pkg`; Go `go list -deps` / `go mod graph`; or read the bundler's own circular-dependency warning. Capture the full ordered chain, e.g. `auth → user → session → auth`, so you are fixing a real edge.
|
|
20
|
+
2. **Find the one edge that is wrong.** A cycle has N edges but usually one of them is the design mistake — a lower-level module reaching back up to a higher-level one, or two leaf-ish modules each grabbing one symbol from the other. With `Grep`, list *exactly which symbols* each module imports from the next in the chain. The edge to break is the one importing the fewest, most-extractable symbols — often a single shared type, constant, or helper.
|
|
21
|
+
3. **Prefer extracting the shared thing into a leaf module — this is the cleanest fix and the most common cause.** If A and B both need a type, constant, or pure helper that currently lives in one of them, move that symbol into a new dependency-free module (`types.ts`, `constants.ts`, `shared/`) that both A and B import *from*, and which imports from neither. The cycle dissolves because the contested symbol no longer lives on the cycle. Update every importer with `Edit`.
|
|
22
|
+
4. **Invert the dependency when there is a true layering violation.** If a lower-level module imports a higher-level one only to call back into it (e.g. a storage layer importing a service to notify it), apply dependency inversion: define the interface/type at the *lower* module (it owns the contract), and have the caller inject the concrete implementation as an argument or via a registration call. The lower module now depends on nothing above it; the arrow points one way.
|
|
23
|
+
5. **Merge the two modules if they are genuinely one unit.** If A and B call deep into each other through many symbols and neither has a coherent identity without the other, they were split artificially. Combine them into one module and re-export from the old paths as a barrel so external callers stay green. A cycle between two files that are really one concept is a packaging bug, not a dependency to invert.
|
|
24
|
+
6. **Defer the import only as a last resort — and say so out loud.** Moving `import` inside the function that uses it (lazy/local import, `require()` at call time, or a TYPE_CHECKING-only import in Python) makes the crash stop because the import now runs after both modules finished loading. It does not remove the cycle — `madge` will still report it. Use it only when the real fixes are blocked (e.g. a third-party constraint), and flag it explicitly as deferring a known design smell.
|
|
25
|
+
7. **Re-run the same tool and check import-time side effects.** Re-run the step-1 command and confirm the cycle no longer appears in its output — that is your proof, not "the crash went away." Then verify nothing relied on import-time side effects whose order you just changed: a module that registered a handler, populated a singleton, or ran top-level code now runs in a new order. Search for top-level statements (not inside a function/class) in the moved code and confirm they still fire when expected.
|
|
26
|
+
|
|
27
|
+
> [!WARNING]
|
|
28
|
+
> A lazy/deferred import "fixes" the crash but leaves the architectural cycle fully in place — the next person hits the same partially-initialized-module bug from a different entry point. Treat it as a tourniquet, not a cure. Always reach for extracting the shared dependency (step 3) or inverting the layer (step 4) first; only defer when those are genuinely blocked, and label it as a deferral.
|
|
29
|
+
|
|
30
|
+
> [!NOTE]
|
|
31
|
+
> The bug is in the import graph, not the stack trace. `cannot access X before initialization` points at the line that *read* the half-built module, which is rarely where the cycle should be cut. Map the graph first (step 1) — the right edge to break is almost never the one the error names.
|
|
32
|
+
|
|
33
|
+
## Output
|
|
34
|
+
|
|
35
|
+
1. **The dependency cycle diagram** — the exact ordered chain from the tool, annotated with the symbols crossing each edge:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
auth.ts ──(needs SessionToken)──▶ session.ts
|
|
39
|
+
▲ │
|
|
40
|
+
└──────(needs currentUser)──────────┘
|
|
41
|
+
Cycle: auth → session → auth (madge --circular)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
2. **The chosen break technique with rationale** — e.g. "Extract `SessionToken` (a type, the only symbol `session` takes from `auth`) into `auth/types.ts` leaf; both import from it. Chosen over deferral because the cycle is a misplaced shared type, not a real layering need."
|
|
45
|
+
|
|
46
|
+
3. **The concrete import/module changes** — the new/edited files and every `import` line that moved, as applied edits (new leaf module created, contested symbol relocated, importers re-pointed).
|
|
47
|
+
|
|
48
|
+
4. **Proof the cycle is gone** — the re-run of the step-1 command showing no cycle, e.g. `madge --circular src` → `✔ No circular dependency found!`, plus a one-line confirmation that any import-time side effects in the moved code still execute in the right order.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "commit-splitter"
|
|
3
|
+
description: "Split one big, mixed-up change into a series of small, atomic commits — each a single logical change that builds and passes tests on its own — by grouping hunks by intent and staging them piecemeal. Use when a working tree or a fat commit mixes a feature, a refactor, a bug fix, and formatting, or before opening a PR you want reviewers to actually read."
|
|
4
|
+
allowed-tools: "Read, Grep, Bash"
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A 600-line diff that mixes a feature, a drive-by refactor, a bug fix, and a formatter run is unreviewable — reviewers skim it and approve on faith. This skill decomposes that change into a sequence of small commits, each one a single logical intent that compiles and passes tests on its own. It groups the diff by purpose, stages one group at a time with `git add -p`, orders them so prerequisites land first, and gives each commit a focused message — so reviewers read the story instead of guessing at it, and `git bisect`/`git revert` stay meaningful.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- An uncommitted working tree mixes concerns — a new feature, an unrelated refactor, a bug fix, and whitespace/formatting churn all tangled together.
|
|
13
|
+
- A single fat commit (yours, not yet pushed) bundles several logical changes and you want to split it before review.
|
|
14
|
+
- You're about to open a PR and want the commit series to read as a deliberate narrative, not a `wip` dump.
|
|
15
|
+
|
|
16
|
+
> [!WARNING]
|
|
17
|
+
> Splitting only pays off if **each** commit independently builds and passes tests. A series where intermediate commits are broken defeats `git bisect` and makes any single-commit `revert` land a non-working tree — worse than one honest fat commit. Verify every commit, not just the tip.
|
|
18
|
+
|
|
19
|
+
## Instructions
|
|
20
|
+
|
|
21
|
+
1. **Inventory what changed.** Run `git status --porcelain` and `git diff --stat` (add `--cached` for staged hunks; `git show --stat HEAD` if splitting an existing commit). Read the actual hunks with `git diff` so you reason about real code, not filenames. Note any new/deleted/renamed files — those move as whole units, not per-hunk.
|
|
22
|
+
2. **Group hunks by logical intent.** Assign every hunk to exactly one group. Typical buckets, in dependency order:
|
|
23
|
+
- **Prerequisite refactor** — renames, extractions, signature changes the feature depends on (no behavior change).
|
|
24
|
+
- **Bug fix** — a self-contained correctness fix, ideally with its own test.
|
|
25
|
+
- **Feature** — the new behavior, built on the refactor above.
|
|
26
|
+
- **Formatting / lint** — pure whitespace, import sorting, autoformatter noise. Isolate this; mixed-in formatting is what makes diffs unreadable.
|
|
27
|
+
- **Unrelated cleanup** — dead code, typo, comment. Its own commit (or a separate PR).
|
|
28
|
+
Watch for **hidden coupling**: a feature that won't compile without the refactor must come *after* it, never before.
|
|
29
|
+
3. **Stage one group at a time.** Use `git add -p <files>` and answer per hunk: `y` to stage, `n` to skip, `s` to split a hunk into smaller pieces. When a single hunk mixes two intents that `s` can't separate (e.g. a logic change and a reformat on adjacent lines), use `git add -e` (or `e` at the prompt) to hand-edit the staged patch — delete the `+`/`-` lines that belong to the other group, keep context lines intact. Stage exactly one group, then go to step 4.
|
|
30
|
+
4. **Verify the staged group in isolation, then commit.** Before committing, prove the staged subset stands alone: `git stash push --keep-index` parks everything *not* staged, leaving only this group in the tree. Run the project's build + tests (detect them — `npm run build && npm test`, `pytest`, `go build ./... && go test ./...`). If it builds and passes, commit (step 6); then `git stash pop` to restore the rest and return to step 3 for the next group. If it fails, you mis-grouped — a prerequisite is in a later group; re-order and re-stage.
|
|
31
|
+
5. **For an already-committed mess, rewrite local history.** Two routes:
|
|
32
|
+
- **Re-stage the whole commit:** `git reset HEAD~1` (soft-ish — keeps changes in the working tree, unstaged), then proceed from step 2 to rebuild it as several commits.
|
|
33
|
+
- **Surgical split inside a series:** `git rebase -i <base>`, mark the offending commit `edit`. When the rebase stops on it, `git reset HEAD~1` to unstage its contents, then split via steps 3–6, and `git rebase --continue`. Use `git rebase --abort` to bail back to the original state if anything looks wrong.
|
|
34
|
+
6. **Write a focused conventional message per commit.** One intent per subject line: `refactor(parser): extract tokenizer`, `fix(auth): reject expired tokens`, `feat(auth): add SSO login`, `style: apply formatter`. The subject names the *single* thing this commit does; if you need "and" or a bullet list of unrelated items, the commit is still mixed — split further.
|
|
35
|
+
7. **Confirm the series reads as a story and every commit is green.** Run `git log --oneline <base>..HEAD` to read the sequence top-to-bottom: prerequisites → fix → feature → cleanup. Then verify *each* commit independently — `git rebase --exec '<build && test>' <base>` replays the series running your command after every commit, failing on the first that breaks. This is the proof that the split is bisect-safe.
|
|
36
|
+
|
|
37
|
+
> [!WARNING]
|
|
38
|
+
> Rewriting history that's already pushed or shared (`reset`, `rebase -i`) forces every collaborator to recover their local copy and can orphan their work. Only reshape **local, unpushed** history. If the commits are already on a shared branch, coordinate first — or leave history alone and split going forward.
|
|
39
|
+
|
|
40
|
+
## Output
|
|
41
|
+
|
|
42
|
+
- **Commit breakdown** — an ordered table: each proposed commit's purpose (its single intent), the files/hunks it claims, and its dependency on earlier commits.
|
|
43
|
+
- **Exact reproduction steps** — the concrete `git add -p` / `git add -e` sequence (or the `rebase -i` + `reset HEAD~1` plan) that produces that breakdown, including the per-group `stash push --keep-index` → build/test → commit → `stash pop` loop.
|
|
44
|
+
- **Recommended commit messages** — one conventional-commit subject (and body where it earns it) per commit, in apply order.
|
|
45
|
+
- **Verification result** — confirmation that `git rebase --exec` ran the build+tests after every commit and the whole series is green, with any commit that needed re-grouping called out.
|
|
46
|
+
|
|
47
|
+
Example breakdown for a tangled working tree:
|
|
48
|
+
|
|
49
|
+
| # | Commit | Hunks / files | Depends on |
|
|
50
|
+
|---|--------|---------------|------------|
|
|
51
|
+
| 1 | `refactor(parser): extract Tokenizer class` | `parser.ts` (lines 12–88), new `tokenizer.ts` | — |
|
|
52
|
+
| 2 | `fix(parser): handle empty input` | `parser.ts` (lines 140–152), `parser.test.ts` (new case) | 1 |
|
|
53
|
+
| 3 | `feat(parser): support inline comments` | `tokenizer.ts` (lines 40–72), `parser.ts` (lines 95–110) | 1 |
|
|
54
|
+
| 4 | `style: apply prettier` | whitespace-only across 6 files | — |
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "dashboard-designer"
|
|
3
|
+
description: "Design a service dashboard that answers one question at a glance — is the service healthy, and if not, where's the problem? — by structuring panels around RED/USE instead of dumping every metric. Use when a service has no dashboard, when the existing one is an unreadable metric wall, or during incident-readiness prep."
|
|
4
|
+
allowed-tools: "Read, Grep, Glob"
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A dashboard is read in two modes: a calm weekly glance, and a 3am incident with an angry pager. Most dashboards are built for neither — they're a wall of every metric the system can emit, ranked by nothing, where the panel that matters is the same size as the one that never moves. This skill designs the opposite: a dashboard structured by a proven method (RED for request services, USE for resources) so the top row answers "is the service healthy?" in one glance, and the rows below answer "then where's the problem?" only when you need them.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
- A service is running in production with no dashboard, or only a default auto-generated one nobody trusts.
|
|
12
|
+
- An existing dashboard is a 40-panel metric dump — technically complete, useless in an incident, because nothing is ranked.
|
|
13
|
+
- Incident-readiness or on-call onboarding: you need a board a new engineer can read cold at 3am.
|
|
14
|
+
- You're defining or visualizing SLOs and need error-budget burn to live next to the signals that drive it.
|
|
15
|
+
- A postmortem found that the dashboard existed but the operator couldn't find the symptom on it fast enough.
|
|
16
|
+
|
|
17
|
+
## Instructions
|
|
18
|
+
1. **Classify the thing you're instrumenting, then pick the method.** Request-driven service (HTTP/gRPC/API) → **RED**: Rate (requests/sec), Errors (failed requests/sec and error %), Duration (latency distribution). Resource or queue (worker pool, broker, DB, cache, thread pool) → **USE**: Utilization (% busy), Saturation (queue depth / backlog / wait time), Errors. A typical service is RED on top with a USE block below for its hottest dependency.
|
|
19
|
+
2. **Put user-facing, SLO-aligned signals in the top row — nothing else competes for that space.** Request rate, error rate (%), latency p95/p99, and **error-budget burn rate** if an SLO exists. These four answer "are users being served?" A reader who sees the top row green should be able to stop reading. Everything below is for when it's red.
|
|
20
|
+
3. **Show latency as percentiles — p50, p95, p99 — never an average.** Average latency is a lie that hides the tail: a p99 of 4s with a 120ms mean reads as "fine" on an average and "users are rage-quitting" on a percentile. Plot p50/p95/p99 as separate series on one panel so the spread between them (the tail blowing out) is visible.
|
|
21
|
+
4. **Place cause metrics BELOW the signals, as drill-down — not mixed in.** CPU, memory, GC pause, queue depth, DB connection pool usage/saturation, downstream dependency latency, restart/OOM counts. These don't tell you if users hurt; they tell you *why* once the top row says they do. Group them so the path is top-down: symptom (top) → suspected cause (below).
|
|
22
|
+
5. **Put correlated panels adjacent so the eye does the joining.** Error rate next to the deploy marker. Latency next to the saturated dependency it's waiting on. Queue depth next to consumer error rate. An operator should be able to see "errors started exactly at the deploy" or "latency tracks the DB pool maxing out" without flipping between boards.
|
|
23
|
+
6. **Annotate the timeline with deploys and incidents.** Wire deploy/release events and incident start/end onto every time-series panel as vertical markers. Half of all "where's the problem?" questions are answered by a deploy line landing on the exact second the graph turns — make that free to see.
|
|
24
|
+
7. **Set thresholds and colors that mean something, plus units and a sane default range.** Color by SLO/alert boundary, not by gut feel: green within budget, amber approaching, red breached — and keep it consistent across panels. Label every axis with units (ms, req/s, %, MiB). Default the time range to something an incident needs (last 1–6h, not 30 days) with the ability to zoom out.
|
|
25
|
+
8. **One dashboard per service or user journey — linked, not merged.** Resist the urge to build one giant board for the whole platform. Per-service boards stay readable; link them (this service → its dependencies' boards, the journey board → each service board) so drill-down is a click, not a scroll through 200 panels.
|
|
26
|
+
9. **Cut every panel that doesn't earn its place.** For each candidate ask: "In an incident, would this change what I do next?" If no, it's decoration — leave it off or push it to a separate deep-dive board. Noise hides signal; a 12-panel board you trust beats a 40-panel board you scan past.
|
|
27
|
+
|
|
28
|
+
> [!WARNING]
|
|
29
|
+
> A dashboard that shows every metric with equal weight is unreadable in an incident — the operator has to reason about *which* panel matters at exactly the moment they have no spare attention. Rank by user impact (RED/USE on top, causes below) or the board is decoration, not a tool.
|
|
30
|
+
|
|
31
|
+
> [!WARNING]
|
|
32
|
+
> Average latency on a dashboard hides the tail where users actually hurt. A healthy-looking mean can sit on top of a p99 that's timing out for 1% of traffic. Always plot percentiles (p50/p95/p99); never let an average latency panel be the thing on-call looks at first.
|
|
33
|
+
|
|
34
|
+
## Output
|
|
35
|
+
- **A top-down layout spec** for one service/journey: the chosen method (RED and/or USE) and the ordered rows — top row of user-facing/SLO signals, then cause/drill-down rows below.
|
|
36
|
+
- **A per-panel table**: panel title → metric/query intent → visualization (time series, single-stat, percentile lines, heatmap) → threshold/color rule → units. Latency panels specify p50/p95/p99.
|
|
37
|
+
- **The annotations and links to wire in**: deploy/incident markers on time-series panels, default time range, and the cross-links to dependency or journey dashboards.
|
|
38
|
+
- **A "cut list"**: panels deliberately left off (and where they live instead), so the omission is a decision, not an oversight.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "deadlock-diagnoser"
|
|
3
|
+
description: "Diagnose a database deadlock from the engine's own deadlock report, reconstruct the lock cycle (A holds 1 wants 2, B holds 2 wants 1), name the root cause — almost always two code paths locking the same rows in different orders — and fix it with consistent lock ordering, shorter transactions, and a retry-the-victim safeguard. Use when the DB logs deadlock errors, when transactions intermittently fail under load, or when queries mysteriously block each other."
|
|
4
|
+
allowed-tools: "Read, Grep, Glob, Bash"
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A deadlock looks random from the application — a transaction that worked a thousand times suddenly errors out under load — but the database already did the forensics for you. When the engine detects a cycle it picks a victim, rolls it back, and logs *exactly* who held what and waited on what. This skill reads that report instead of guessing: it pulls the Postgres deadlock log lines (or the SQL Server deadlock graph / `innodb status` in MySQL), reconstructs the cycle (A holds lock 1 and wants lock 2 while B holds 2 and wants 1), and names the real root cause — which is almost always two code paths acquiring the **same** rows or tables in **different** orders. Then it fixes the cause: enforce one consistent lock-acquisition order everywhere, shrink the lock window so the race rarely opens, and add a retry-the-victim safeguard for the deadlocks you can't design away — in that priority, because retries without ordering just trade a deadlock for a rollback storm.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- The database log shows `deadlock detected` (Postgres), a deadlock graph / error 1205 (SQL Server), or `Deadlock found when trying to get lock` (MySQL/InnoDB).
|
|
13
|
+
- A transaction intermittently fails or auto-retries only under concurrency — fine in dev, flaky in production at peak.
|
|
14
|
+
- Two queries or endpoints mysteriously block each other, or you see processes stuck in a lock wait that times out.
|
|
15
|
+
- You're adding a write path that touches multiple rows/tables and want to confirm it locks in the same order as existing code before it ships.
|
|
16
|
+
- Lock contention (not a true cycle) is serializing throughput, and you need to tell genuine deadlocks apart from long lock waits.
|
|
17
|
+
|
|
18
|
+
## Instructions
|
|
19
|
+
|
|
20
|
+
1. **Get the engine's deadlock report — don't reconstruct from app logs.** In Postgres, read the server log around the error: it prints both processes, their full SQL statements, and the `Process N waits for <lockmode> on <relation/tuple>; blocked by process M` lines for each side of the cycle (raise `log_lock_waits = on` and `deadlock_timeout` context if it's terse). In SQL Server, pull the deadlock graph from the `system_health` Extended Events session or a trace — it lists each `process` with its `inputbuf` (the statement) and the `resource-list` of locks owned vs. requested. In MySQL/InnoDB, run `SHOW ENGINE INNODB STATUS` and read the `LATEST DETECTED DEADLOCK` section. This report is ground truth; the app's stack trace only tells you which transaction lost.
|
|
21
|
+
2. **Reconstruct the cycle explicitly: who HELD what, who WANTED what.** Write it out as a two-column picture — `Txn A: holds <lock on resource 1>, waits for <lock on resource 2>` / `Txn B: holds <lock on resource 2>, waits for <lock on resource 1>`. Identify the exact resources (which rows/index ranges/tables) and the lock modes (row `FOR UPDATE`/exclusive vs. shared, gap locks in InnoDB, intent locks in SQL Server). A real deadlock is a closed cycle of waits; if it's not a cycle, it's lock contention or a lock-wait timeout (step 8), which has a different fix.
|
|
22
|
+
3. **Find the inconsistent acquisition ORDER — the usual root cause.** Grep the codebase for every transaction that touches the resources in the cycle and trace the order each one locks them. The classic bug: one path does `UPDATE accounts WHERE id=1` then `id=2`, another does `id=2` then `id=1` (or two services lock tables `orders` then `inventory` vs. `inventory` then `orders`). Watch for ordering that's *hidden* — a `SELECT ... FOR UPDATE` with an unordered `IN (...)` or a join whose row-locking order depends on the plan, an ORM that emits writes in object-graph order, or a foreign-key check that takes a lock on the parent row you didn't write explicitly.
|
|
23
|
+
4. **Fix the cause first: enforce ONE consistent lock-acquisition order across all transactions.** Make every code path acquire the shared resources in the same deterministic order — sort the ids before locking (`SELECT ... FOR UPDATE ... ORDER BY id`), always lock parent before child, always lock tables in a fixed documented sequence. Consistent ordering makes a cycle impossible: contenders queue instead of deadlocking. This is the only fix that actually removes the deadlock rather than reducing its odds.
|
|
24
|
+
5. **Shrink the lock window so the race rarely opens.** Keep transactions short and narrow: acquire locks as late as possible, commit as early as possible, and lock only the rows you'll write. Never hold a transaction open across a network/RPC/third-party-API call or across user think-time — an external call inside the transaction stretches the lock-hold from milliseconds to seconds and turns rare contention into constant deadlocks. Do the slow work *before* `BEGIN` or *after* `COMMIT`.
|
|
25
|
+
6. **Pick a deliberate lock strategy for the access pattern, and right-size isolation.** Where the same rows are contended, use pessimistic locking with `SELECT ... FOR UPDATE` in the consistent order from step 4. Where conflicts are *rare*, prefer optimistic concurrency — a `version`/`updated_at` column checked in the `WHERE` of the `UPDATE` and a conflict-retry, which takes no long-held locks. If the engine is over-locking (e.g. Serializable or InnoDB gap locks causing deadlocks on inserts/range scans), drop to the lowest isolation level that's still correct (often Read Committed) to acquire fewer locks.
|
|
26
|
+
7. **Add the retry-the-victim safeguard — last, not first.** A deadlock victim's transaction is rolled back cleanly and is a *transient, safe-to-retry* error; the app should catch it specifically (Postgres `SQLSTATE 40P01`, MySQL `1213`, SQL Server `1205`) and retry the whole transaction with capped exponential backoff and jitter (e.g. 3–5 attempts). Retry the *entire* transaction from `BEGIN` — replaying half a rolled-back transaction corrupts state. This handles the deadlocks you can't design away; it does NOT substitute for steps 4–5.
|
|
27
|
+
8. **Distinguish a true deadlock from plain lock contention before "fixing" the wrong thing.** If the report shows a lock-*wait timeout* rather than a detected cycle, there's no ordering bug — one transaction is simply holding a lock too long (a long-running write, an idle-in-transaction connection, a missing index forcing a wide row/range lock). The fix there is shortening the holder (step 5), adding the index so the lock is narrow (`query-plan-analyzer`), or killing idle-in-transaction sessions — not reordering locks.
|
|
28
|
+
|
|
29
|
+
> [!WARNING]
|
|
30
|
+
> Adding retries WITHOUT fixing the inconsistent lock order just papers over the bug. Under load, every retry re-enters the same cycle, so you trade one deadlock for a storm of rollbacks and re-runs: throughput craters, latency spikes, and the database burns work undoing transactions. Fix the ordering first; the retry is a net for the residual, not the cure.
|
|
31
|
+
|
|
32
|
+
> [!WARNING]
|
|
33
|
+
> A transaction that holds a lock across an external/API call (or user think-time) is the single most common way rare contention becomes constant deadlocks — the lock-hold goes from milliseconds to seconds, widening the race window enormously. Move every network call and slow computation outside the `BEGIN ... COMMIT`.
|
|
34
|
+
|
|
35
|
+
> [!NOTE]
|
|
36
|
+
> Lowering isolation reduces locking but changes correctness guarantees (Read Committed allows non-repeatable reads; dropping below Serializable can reintroduce write skew). Only lower it where the access pattern is provably safe — don't trade a deadlock for a silent data anomaly.
|
|
37
|
+
|
|
38
|
+
## Output
|
|
39
|
+
|
|
40
|
+
A short report with four parts:
|
|
41
|
+
|
|
42
|
+
1. **The reconstructed cycle** — quoted from the engine's deadlock report: `Txn A holds <lock on R1>, wants <lock on R2>` / `Txn B holds <lock on R2>, wants <lock on R1>`, with the exact resources, lock modes, and the two offending statements.
|
|
43
|
+
2. **The root cause** — the specific inconsistent lock-acquisition order (or over-long lock scope / over-strict isolation) behind the cycle, naming the two code paths and the resources they lock in conflicting order.
|
|
44
|
+
3. **The fix** — one concrete change: the consistent ordering to enforce (with the exact `ORDER BY` / lock sequence), or the shortened-transaction change (what to move outside `BEGIN`), or the isolation-level / locking-strategy change — not a menu.
|
|
45
|
+
4. **The retry safeguard** — the specific deadlock SQLSTATE/error code to catch and the backoff retry of the whole transaction, framed explicitly as the net for residual deadlocks, not the primary fix.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "feature-flag-retirer"
|
|
3
|
+
description: "Retire stale feature flags by confirming each flag's decided final state, then collapsing every conditional to the winning branch and deleting the loser plus the now-dead code it reached. Use when temporary flags have outlived their rollout, when flag conditionals clutter the code, or during a flag-debt cleanup."
|
|
4
|
+
allowed-tools: "Read, Grep, Glob, Edit"
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Feature flags are born temporary and die permanent. Once a flag is fully rolled out or quietly abandoned, the `if (flag)` it guards is just branching debt — two code paths where one is now unreachable. This skill retires a flag for real: it pins down which branch actually won, finds *every* reference (not just the obvious helper call), collapses each conditional to the winner, and deletes the loser along with any code only the dead branch reached — one flag at a time, with tests green after each.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- A flag meant to last a sprint has been at 100% (or 0%) for months and still litters the code with conditionals.
|
|
13
|
+
- Flag checks have multiplied — nested `if (flagA && !flagB)` paths nobody can reason about — and you want to pay down the debt.
|
|
14
|
+
- You're running a flag-debt cleanup and need each removal to be independently reviewable and revertible.
|
|
15
|
+
|
|
16
|
+
> [!WARNING]
|
|
17
|
+
> Verify the flag's *decided* final state before you collapse anything. "Currently 100%" is not "permanently on" — a flag mid-rollout, a kill-switch, or an experiment still gathering data must NOT be retired. Deleting the live branch ships or kills a feature: that's a production incident, not a cleanup. Confirm from the flag system/config AND a human owner that the decision is final, and which branch won.
|
|
18
|
+
|
|
19
|
+
## Instructions
|
|
20
|
+
|
|
21
|
+
1. **Pin down the decided final state — not the current value.** For the flag, answer one question: is it *permanently on* (fully rolled out, winner = enabled branch) or *abandoned* (will never ship, winner = disabled branch)? Read the flag config/dashboard, then confirm with the owner. Reject the flag from this pass if it's still rolling out, A/B testing, a kill-switch kept for emergencies, or used per-tenant/per-environment with different values — those are live, not stale.
|
|
22
|
+
2. **Find every reference — grep the flag KEY, not just the helper.** A flag leaks far past its `if`. Search the whole repo for the literal flag key string and its identifier:
|
|
23
|
+
- the helper calls: `isEnabled("new_checkout")`, `flags.newCheckout`, `useFlag(...)`, `treatment(...)`;
|
|
24
|
+
- the flag *definition/registration* (the declarations file, defaults, env vars, IaC/config);
|
|
25
|
+
- tests, fixtures, and mocks that force the flag on or off;
|
|
26
|
+
- analytics/telemetry events fired only when on, and feature-gated schema/migrations/routes;
|
|
27
|
+
- string usages: config keys, JSON, YAML, query params, log lines, docs.
|
|
28
|
+
Grep both the key (`"new_checkout"`) and the symbol (`newCheckout`) — different layers spell it differently.
|
|
29
|
+
3. **Collapse each conditional to the winning branch.** For every reference, rewrite the conditional to keep only the winner: fully-on → keep the `if` body, drop the `else`/fallback; abandoned → keep the `else`, delete the guarded body. Remove the now-constant condition entirely — no `if (true)`, no dead `else`. Flatten the indentation you just freed.
|
|
30
|
+
4. **Delete the code only the dead branch reached.** A removed branch usually calls helpers, imports, components, or fires events that nothing else uses. Trace each symbol the loser referenced; if its only caller was the branch you just deleted, remove it too (and repeat transitively). This is where flag retirement leaves dangling dead code if you stop at the `if`.
|
|
31
|
+
5. **Remove the flag's definition and its tests.** Delete the flag declaration/registration, its default value and env/config entries, and the tests/fixtures that existed solely to toggle it. Tests that asserted the *winning* behavior stay — but drop their flag-setup boilerplate so they test the now-unconditional path.
|
|
32
|
+
6. **One flag at a time, tests green after each.** Never retire two flags in one pass. After each flag: run the build and test suite, confirm green, and keep it as a single commit. A revert then removes exactly one flag's worth of change with no collateral.
|
|
33
|
+
|
|
34
|
+
> [!WARNING]
|
|
35
|
+
> A flag almost always guards MORE than the obvious if-block — feature-gated helper functions, config defaults, DB columns or migrations, route registrations, and analytics events reachable only when on. Grep exhaustively (step 2) before deleting: stop at the `if` and you leave dangling dead code; over-trust a single grep and you delete a path the *winning* branch still uses. When in doubt whether a symbol is shared, keep it and flag it for review.
|
|
36
|
+
|
|
37
|
+
## Output
|
|
38
|
+
|
|
39
|
+
For each retired flag, a record an owner can rubber-stamp:
|
|
40
|
+
|
|
41
|
+
- **Confirmed final state** — `permanently-on` or `abandoned`, with the source (flag dashboard value + owner sign-off) and the resulting winning branch.
|
|
42
|
+
- **Reference inventory** — every match for the key and symbol, grouped by layer: conditionals, definition/config, tests/fixtures, analytics, schema/routes, docs/strings.
|
|
43
|
+
- **Collapse plan** — per conditional: which branch wins, the resulting diff, and the list of now-dead symbols deleted because only the loser reached them.
|
|
44
|
+
- **Verification** — confirmation the build and test suite pass after the removal, and that the change is a single self-contained commit. Anything ambiguous (shared symbol, public-API surface, flag still live elsewhere) is listed as a manual-review item rather than deleted.
|