claude-code-superkit 0.0.0-stage → 1.5.3
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/LICENSE +21 -0
- package/README.md +374 -2
- package/bin/cli.js +153 -0
- package/bin/inject-tokens.js +224 -0
- package/bin/measure-tokens.js +17 -0
- package/bin/superkit-counts-verify.sh +30 -0
- package/lib/codex.js +246 -0
- package/lib/docs-scaffold.js +147 -0
- package/lib/installer.js +499 -0
- package/lib/prompts.js +51 -0
- package/lib/settings-builder.js +51 -0
- package/lib/superpowers.js +99 -0
- package/lib/utils.js +117 -0
- package/lib/validator.js +84 -0
- package/package.json +49 -4
- package/packages/codex/AGENTS.md +396 -0
- package/packages/codex/INSTALL.md +342 -0
- package/packages/codex/config.toml +22 -0
- package/packages/codex/hooks/superkit.mjs +203 -0
- package/packages/codex/hooks.json +30 -0
- package/packages/codex/legacy-baseline.json +88 -0
- package/packages/codex/native-skills.txt +65 -0
- package/packages/codex/rules/default.rules +565 -0
- package/packages/codex/skills/ai-slop-cleaner/SKILL.md +44 -0
- package/packages/codex/skills/api-contract-sync/SKILL.md +96 -0
- package/packages/codex/skills/architect/SKILL.md +49 -0
- package/packages/codex/skills/audit-backend/SKILL.md +80 -0
- package/packages/codex/skills/audit-frontend/SKILL.md +82 -0
- package/packages/codex/skills/audit-infra/SKILL.md +87 -0
- package/packages/codex/skills/audit-orchestrator/SKILL.md +114 -0
- package/packages/codex/skills/behavioral-nudge-engine/SKILL.md +110 -0
- package/packages/codex/skills/benchmark/SKILL.md +49 -0
- package/packages/codex/skills/bot-reviewer/SKILL.md +94 -0
- package/packages/codex/skills/code-reviewer/SKILL.md +67 -0
- package/packages/codex/skills/codebase-onboarding-engineer/SKILL.md +46 -0
- package/packages/codex/skills/comment-rot-analyzer/SKILL.md +75 -0
- package/packages/codex/skills/commit-helper/SKILL.md +101 -0
- package/packages/codex/skills/critic/SKILL.md +56 -0
- package/packages/codex/skills/database-reviewer/SKILL.md +79 -0
- package/packages/codex/skills/debug-observer/SKILL.md +107 -0
- package/packages/codex/skills/dependency-checker/SKILL.md +69 -0
- package/packages/codex/skills/design-system-reviewer/SKILL.md +121 -0
- package/packages/codex/skills/dev-orchestrator/SKILL.md +304 -0
- package/packages/codex/skills/docs-reviewer/SKILL.md +86 -0
- package/packages/codex/skills/drizzle-orm-expert/SKILL.md +246 -0
- package/packages/codex/skills/e2e-test-generator/SKILL.md +95 -0
- package/packages/codex/skills/evaluator/SKILL.md +63 -0
- package/packages/codex/skills/frontend-perf-reviewer/SKILL.md +95 -0
- package/packages/codex/skills/frontend-ui-color-reviewer/SKILL.md +68 -0
- package/packages/codex/skills/frontend-ui-design-critic/SKILL.md +99 -0
- package/packages/codex/skills/frontend-ui-interaction-reviewer/SKILL.md +102 -0
- package/packages/codex/skills/frontend-ui-motion-reviewer/SKILL.md +85 -0
- package/packages/codex/skills/frontend-ui-reviewer/SKILL.md +81 -0
- package/packages/codex/skills/frontend-ui-typography-reviewer/SKILL.md +62 -0
- package/packages/codex/skills/gltf-debugging/SKILL.md +128 -0
- package/packages/codex/skills/go-benchmark/SKILL.md +217 -0
- package/packages/codex/skills/go-concurrency-reviewer/SKILL.md +113 -0
- package/packages/codex/skills/go-error-reviewer/SKILL.md +107 -0
- package/packages/codex/skills/go-grpc-patterns/SKILL.md +285 -0
- package/packages/codex/skills/go-modernizer/SKILL.md +111 -0
- package/packages/codex/skills/go-observability-reviewer/SKILL.md +121 -0
- package/packages/codex/skills/go-performance-reviewer/SKILL.md +112 -0
- package/packages/codex/skills/go-reviewer/SKILL.md +109 -0
- package/packages/codex/skills/go-samber-do/SKILL.md +158 -0
- package/packages/codex/skills/go-samber-lo/SKILL.md +178 -0
- package/packages/codex/skills/go-samber-oops/SKILL.md +174 -0
- package/packages/codex/skills/goal-verifier/SKILL.md +53 -0
- package/packages/codex/skills/health-checker/SKILL.md +109 -0
- package/packages/codex/skills/html-to-3d-texture/SKILL.md +153 -0
- package/packages/codex/skills/impeccable-craft/SKILL.md +101 -0
- package/packages/codex/skills/lint-runner/SKILL.md +85 -0
- package/packages/codex/skills/migrate/SKILL.md +60 -0
- package/packages/codex/skills/migration-reviewer/SKILL.md +88 -0
- package/packages/codex/skills/minimal-change-engineer/SKILL.md +44 -0
- package/packages/codex/skills/new-migration/SKILL.md +59 -0
- package/packages/codex/skills/nextjs-supabase-auth/SKILL.md +227 -0
- package/packages/codex/skills/output-enforcement/SKILL.md +71 -0
- package/packages/codex/skills/plan-checker/SKILL.md +53 -0
- package/packages/codex/skills/postgresql-optimization/SKILL.md +159 -0
- package/packages/codex/skills/pre-deploy-validator/SKILL.md +111 -0
- package/packages/codex/skills/presentation-reviewer/SKILL.md +88 -0
- package/packages/codex/skills/product-3d-lighting/SKILL.md +137 -0
- package/packages/codex/skills/project-architecture/SKILL.md +52 -0
- package/packages/codex/skills/py-reviewer/SKILL.md +100 -0
- package/packages/codex/skills/r3f-scene-reviewer/SKILL.md +88 -0
- package/packages/codex/skills/r3f-scroll-driven-3d/SKILL.md +162 -0
- package/packages/codex/skills/reality-checker/SKILL.md +61 -0
- package/packages/codex/skills/redis-patterns/SKILL.md +260 -0
- package/packages/codex/skills/review-orchestrator/SKILL.md +151 -0
- package/packages/codex/skills/rs-reviewer/SKILL.md +98 -0
- package/packages/codex/skills/ru-text/SKILL.md +132 -0
- package/packages/codex/skills/scaffold-endpoint/SKILL.md +44 -0
- package/packages/codex/skills/security-scanner/SKILL.md +94 -0
- package/packages/codex/skills/silent-failure-hunter/SKILL.md +176 -0
- package/packages/codex/skills/telegram-bot-builder/SKILL.md +120 -0
- package/packages/codex/skills/test-generator/SKILL.md +79 -0
- package/packages/codex/skills/test-runner/SKILL.md +101 -0
- package/packages/codex/skills/threejs-color-management/SKILL.md +97 -0
- package/packages/codex/skills/tree-generator/SKILL.md +63 -0
- package/packages/codex/skills/ts-reviewer/SKILL.md +90 -0
- package/packages/codex/skills/ui-design-reviewer/SKILL.md +89 -0
- package/packages/codex/skills/ui-reviewer/SKILL.md +103 -0
- package/packages/codex/skills/visual-reviewer/SKILL.md +85 -0
- package/packages/codex/skills/writing-agents/SKILL.md +165 -0
- package/packages/codex/skills/writing-commands/SKILL.md +173 -0
- package/packages/core/CLAUDE.md +118 -0
- package/packages/core/INTERNAL-FILES +16 -0
- package/packages/core/agents/ai-slop-cleaner.md +187 -0
- package/packages/core/agents/api-contract-sync.md +127 -0
- package/packages/core/agents/architect.md +205 -0
- package/packages/core/agents/audit-backend.md +137 -0
- package/packages/core/agents/audit-frontend.md +129 -0
- package/packages/core/agents/audit-infra.md +182 -0
- package/packages/core/agents/behavioral-nudge-engine.md +170 -0
- package/packages/core/agents/code-reviewer.md +195 -0
- package/packages/core/agents/codebase-onboarding-engineer.md +172 -0
- package/packages/core/agents/comment-rot-analyzer.md +115 -0
- package/packages/core/agents/critic.md +172 -0
- package/packages/core/agents/database-reviewer.md +201 -0
- package/packages/core/agents/debug-observer.md +261 -0
- package/packages/core/agents/dependency-checker.md +199 -0
- package/packages/core/agents/docs-reviewer.md +206 -0
- package/packages/core/agents/e2e-test-generator.md +261 -0
- package/packages/core/agents/evaluator.md +141 -0
- package/packages/core/agents/goal-verifier.md +130 -0
- package/packages/core/agents/health-checker.md +194 -0
- package/packages/core/agents/migration-reviewer.md +161 -0
- package/packages/core/agents/minimal-change-engineer.md +219 -0
- package/packages/core/agents/plan-checker.md +139 -0
- package/packages/core/agents/pre-deploy-validator.md +189 -0
- package/packages/core/agents/reality-checker.md +175 -0
- package/packages/core/agents/scaffold-endpoint.md +162 -0
- package/packages/core/agents/security-scanner.md +236 -0
- package/packages/core/agents/silent-failure-hunter.md +276 -0
- package/packages/core/agents/test-generator.md +146 -0
- package/packages/core/agents/tree-generator.md +141 -0
- package/packages/core/agents/ui-reviewer.md +172 -0
- package/packages/core/agents/visual-reviewer.md +211 -0
- package/packages/core/commands/audit.md +166 -0
- package/packages/core/commands/benchmark.md +130 -0
- package/packages/core/commands/commit.md +148 -0
- package/packages/core/commands/dev.md +302 -0
- package/packages/core/commands/docs-init.md +75 -0
- package/packages/core/commands/lint.md +137 -0
- package/packages/core/commands/migrate.md +146 -0
- package/packages/core/commands/new-migration.md +182 -0
- package/packages/core/commands/pair.md +209 -0
- package/packages/core/commands/review.md +195 -0
- package/packages/core/commands/security-scan.md +142 -0
- package/packages/core/commands/superkit-evolve.md +231 -0
- package/packages/core/commands/superkit-init.md +274 -0
- package/packages/core/commands/test.md +108 -0
- package/packages/core/commands/workflow.md +150 -0
- package/packages/core/docs/guide/01-getting-started.md +41 -0
- package/packages/core/docs-templates/README.md +33 -0
- package/packages/core/docs-templates/adr-template.md +62 -0
- package/packages/core/docs-templates/architecture/api-contracts.md +26 -0
- package/packages/core/docs-templates/architecture/api-reference.md +48 -0
- package/packages/core/docs-templates/architecture/auth-and-sessions.md +38 -0
- package/packages/core/docs-templates/architecture/backend-layers.md +42 -0
- package/packages/core/docs-templates/architecture/data-flow.md +26 -0
- package/packages/core/docs-templates/architecture/database-schema.md +51 -0
- package/packages/core/docs-templates/architecture/deployment.md +41 -0
- package/packages/core/docs-templates/architecture/frontend-state.md +46 -0
- package/packages/core/helpers/statusline.cjs +370 -0
- package/packages/core/hooks/CHAIN-SEMANTICS.md +84 -0
- package/packages/core/hooks/audit-settings-source.sh +98 -0
- package/packages/core/hooks/audit-trail.sh +120 -0
- package/packages/core/hooks/block-dangerous-git.sh +189 -0
- package/packages/core/hooks/bundle-import-check.sh +64 -0
- package/packages/core/hooks/compact-state-inject.sh +84 -0
- package/packages/core/hooks/config-protection.sh +68 -0
- package/packages/core/hooks/console-log-warning.sh +28 -0
- package/packages/core/hooks/context-monitor.sh +35 -0
- package/packages/core/hooks/dev-edit-counter.sh +59 -0
- package/packages/core/hooks/dev-marker-set.sh +55 -0
- package/packages/core/hooks/dev-required-on-commit.sh +426 -0
- package/packages/core/hooks/doc-check-on-commit.sh +396 -0
- package/packages/core/hooks/edit-streak-check.sh +63 -0
- package/packages/core/hooks/evolve-check.sh +78 -0
- package/packages/core/hooks/gateguard-pre-edit.sh +154 -0
- package/packages/core/hooks/gateguard-record-facts.sh +37 -0
- package/packages/core/hooks/intake-classifier.py +449 -0
- package/packages/core/hooks/lib/profile.sh +141 -0
- package/packages/core/hooks/loop-guard.sh +75 -0
- package/packages/core/hooks/migration-safety.sh +56 -0
- package/packages/core/hooks/plan-completion-gate.sh +61 -0
- package/packages/core/hooks/pre-compact-save.sh +89 -0
- package/packages/core/hooks/security-patterns.sh +119 -0
- package/packages/core/hooks/session-context-restore.sh +76 -0
- package/packages/core/hooks/subagent-stop-validate.sh +73 -0
- package/packages/core/hooks/superkit-counts-verify.sh +346 -0
- package/packages/core/hooks/superkit-update.sh +367 -0
- package/packages/core/hooks/tests/README.md +42 -0
- package/packages/core/hooks/tests/audit-trail-chain_test.sh +62 -0
- package/packages/core/hooks/tests/block-dangerous-git_test.sh +144 -0
- package/packages/core/hooks/tests/chain-semantics/h1.sh +11 -0
- package/packages/core/hooks/tests/chain-semantics/h2.sh +9 -0
- package/packages/core/hooks/tests/chain-semantics/h3.sh +11 -0
- package/packages/core/hooks/tests/chain-semantics/run-experiment.sh +65 -0
- package/packages/core/hooks/tests/chain-semantics/settings-experiment.json +15 -0
- package/packages/core/hooks/tests/counts-verify_test.sh +118 -0
- package/packages/core/hooks/tests/dev-required-on-commit_test.sh +207 -0
- package/packages/core/hooks/tests/dev-required-wip-on-main_test.sh +20 -0
- package/packages/core/hooks/tests/doc-check-on-commit_test.sh +122 -0
- package/packages/core/hooks/tests/edit-streak-check_test.sh +106 -0
- package/packages/core/hooks/tests/internal-manifest_test.sh +130 -0
- package/packages/core/hooks/tests/json-path_test.sh +108 -0
- package/packages/core/hooks/tests/mirror-invariants_test.sh +139 -0
- package/packages/core/hooks/tests/nudge-throttle_test.sh +95 -0
- package/packages/core/hooks/tests/profile-helper_test.sh +21 -0
- package/packages/core/hooks/tests/run-all.sh +47 -0
- package/packages/core/hooks/tests/session-key-helper_test.sh +17 -0
- package/packages/core/hooks/tests/superkit-update_test.sh +311 -0
- package/packages/core/hooks/tests/warn-visibility_test.sh +59 -0
- package/packages/core/hooks/user-intent-detect.sh +44 -0
- package/packages/core/hooks/verify-hooks.sh +69 -0
- package/packages/core/rules/auto-commands.md +124 -0
- package/packages/core/rules/coding-style.md +68 -0
- package/packages/core/rules/dev-workflow.md +77 -0
- package/packages/core/rules/documentation.md +106 -0
- package/packages/core/rules/frontend-aesthetics.md +53 -0
- package/packages/core/rules/git-workflow.md +14 -0
- package/packages/core/rules/security.md +29 -0
- package/packages/core/rules/superkit-integrity.md +114 -0
- package/packages/core/settings.json +229 -0
- package/packages/core/skills/drizzle-orm-expert/SKILL.md +260 -0
- package/packages/core/skills/nextjs-supabase-auth/SKILL.md +259 -0
- package/packages/core/skills/postgresql-optimization/SKILL.md +150 -0
- package/packages/core/skills/project-architecture/SKILL.md +31 -0
- package/packages/core/skills/project-scanner/SKILL.md +132 -0
- package/packages/core/skills/redis-patterns/SKILL.md +261 -0
- package/packages/core/skills/ru-text/SKILL.md +156 -0
- package/packages/core/skills/telegram-bot-builder/SKILL.md +145 -0
- package/packages/core/skills/writing-agents/SKILL.md +220 -0
- package/packages/core/skills/writing-commands/SKILL.md +176 -0
- package/packages/core/skills/writing-hooks/SKILL.md +335 -0
- package/packages/extras/bot-reviewer.md +279 -0
- package/packages/extras/design-system-reviewer.md +234 -0
- package/packages/extras/red-blue-auditor/README.md +117 -0
- package/packages/extras/red-blue-auditor/agent.md +128 -0
- package/packages/extras/red-blue-auditor/patterns/agent-config.txt +4 -0
- package/packages/extras/red-blue-auditor/patterns/hook-injection.txt +8 -0
- package/packages/extras/red-blue-auditor/patterns/mcp-risk.txt +5 -0
- package/packages/extras/red-blue-auditor/patterns/permission-abuse.txt +6 -0
- package/packages/extras/red-blue-auditor/patterns/secrets.txt +17 -0
- package/packages/extras/red-blue-auditor/scan.sh +88 -0
- package/packages/extras/skillsmp-search/SKILL.md +134 -0
- package/packages/frontend-3d/README.md +62 -0
- package/packages/frontend-3d/agents/frontend-perf-reviewer.md +164 -0
- package/packages/frontend-3d/agents/presentation-reviewer.md +146 -0
- package/packages/frontend-3d/agents/r3f-scene-reviewer.md +128 -0
- package/packages/frontend-3d/agents/ui-design-reviewer.md +169 -0
- package/packages/frontend-3d/commands/capture-screen.md +166 -0
- package/packages/frontend-3d/hooks/bundle-size-warn.sh +93 -0
- package/packages/frontend-3d/hooks/gsap-pattern-check.sh +74 -0
- package/packages/frontend-3d/hooks/r3f-color-check.sh +76 -0
- package/packages/frontend-3d/hooks/tailwind-version-guard.sh +98 -0
- package/packages/frontend-3d/rules/frontend-aesthetics-3d.md +81 -0
- package/packages/frontend-3d/rules/gsap-conventions.md +129 -0
- package/packages/frontend-3d/rules/threejs-conventions.md +116 -0
- package/packages/frontend-3d/skills/gltf-debugging/SKILL.md +145 -0
- package/packages/frontend-3d/skills/html-to-3d-texture/SKILL.md +183 -0
- package/packages/frontend-3d/skills/output-enforcement/SKILL.md +79 -0
- package/packages/frontend-3d/skills/product-3d-lighting/SKILL.md +166 -0
- package/packages/frontend-3d/skills/r3f-scroll-driven-3d/SKILL.md +209 -0
- package/packages/frontend-3d/skills/threejs-color-management/SKILL.md +111 -0
- package/packages/frontend-ui/NOTICE.md +99 -0
- package/packages/frontend-ui/README.md +59 -0
- package/packages/frontend-ui/agents/ui-color-reviewer.md +174 -0
- package/packages/frontend-ui/agents/ui-design-critic.md +198 -0
- package/packages/frontend-ui/agents/ui-interaction-reviewer.md +259 -0
- package/packages/frontend-ui/agents/ui-motion-reviewer.md +266 -0
- package/packages/frontend-ui/agents/ui-reviewer.md +251 -0
- package/packages/frontend-ui/agents/ui-typography-reviewer.md +222 -0
- package/packages/frontend-ui/hooks/tests/ui-animation-easing-check_test.sh +78 -0
- package/packages/frontend-ui/hooks/tests/ui-banned-fonts-check_test.sh +77 -0
- package/packages/frontend-ui/hooks/tests/ui-color-check_test.sh +75 -0
- package/packages/frontend-ui/hooks/ui-animation-easing-check.sh +86 -0
- package/packages/frontend-ui/hooks/ui-banned-fonts-check.sh +90 -0
- package/packages/frontend-ui/hooks/ui-color-check.sh +102 -0
- package/packages/frontend-ui/rules/color-and-contrast.md +230 -0
- package/packages/frontend-ui/rules/frontend-design-aesthetics.md +124 -0
- package/packages/frontend-ui/rules/interaction-polish.md +335 -0
- package/packages/frontend-ui/rules/motion-and-animation.md +283 -0
- package/packages/frontend-ui/rules/spatial-and-layout.md +212 -0
- package/packages/frontend-ui/rules/typography-guidelines.md +185 -0
- package/packages/frontend-ui/rules/ui-anti-patterns.md +309 -0
- package/packages/frontend-ui/settings.json +24 -0
- package/packages/frontend-ui/skills/impeccable-craft/SKILL.md +187 -0
- package/packages/gan/README.md +232 -0
- package/packages/gan/agents/gan-evaluator.md +214 -0
- package/packages/gan/agents/gan-generator.md +171 -0
- package/packages/gan/agents/gan-planner.md +206 -0
- package/packages/gan/rubrics/functionality.md +66 -0
- package/packages/gan/rubrics/ui-quality.md +77 -0
- package/packages/gan/skills/gan-evaluator/SKILL.md +91 -0
- package/packages/gan/skills/gan-generator/SKILL.md +72 -0
- package/packages/gan/skills/gan-planner/SKILL.md +93 -0
- package/packages/stack-agents/go/go-concurrency-reviewer.md +210 -0
- package/packages/stack-agents/go/go-error-reviewer.md +187 -0
- package/packages/stack-agents/go/go-modernizer.md +160 -0
- package/packages/stack-agents/go/go-observability-reviewer.md +185 -0
- package/packages/stack-agents/go/go-performance-reviewer.md +160 -0
- package/packages/stack-agents/go/go-reviewer.md +277 -0
- package/packages/stack-agents/go/references/benchmark-methodology.md +280 -0
- package/packages/stack-agents/go/references/channel-patterns.md +384 -0
- package/packages/stack-agents/go/references/cli-cobra-viper.md +190 -0
- package/packages/stack-agents/go/references/code-style.md +262 -0
- package/packages/stack-agents/go/references/context-propagation.md +317 -0
- package/packages/stack-agents/go/references/data-structures.md +315 -0
- package/packages/stack-agents/go/references/database-patterns.md +334 -0
- package/packages/stack-agents/go/references/design-patterns.md +411 -0
- package/packages/stack-agents/go/references/di-frameworks.md +146 -0
- package/packages/stack-agents/go/references/error-creation.md +251 -0
- package/packages/stack-agents/go/references/error-inspection.md +348 -0
- package/packages/stack-agents/go/references/error-wrapping.md +230 -0
- package/packages/stack-agents/go/references/gopls-driving.md +62 -0
- package/packages/stack-agents/go/references/goroutine-lifecycle.md +351 -0
- package/packages/stack-agents/go/references/graphql-patterns.md +221 -0
- package/packages/stack-agents/go/references/grpc-patterns.md +373 -0
- package/packages/stack-agents/go/references/modernize-guide.md +522 -0
- package/packages/stack-agents/go/references/module-management.md +191 -0
- package/packages/stack-agents/go/references/naming-conventions.md +176 -0
- package/packages/stack-agents/go/references/observability-pipeline.md +352 -0
- package/packages/stack-agents/go/references/performance-profiling.md +250 -0
- package/packages/stack-agents/go/references/refactoring-mechanics.md +127 -0
- package/packages/stack-agents/go/references/rest-openapi-patterns.md +160 -0
- package/packages/stack-agents/go/references/samber-do.md +201 -0
- package/packages/stack-agents/go/references/samber-libraries.md +363 -0
- package/packages/stack-agents/go/references/samber-lo.md +260 -0
- package/packages/stack-agents/go/references/samber-oops.md +200 -0
- package/packages/stack-agents/go/references/security-checklist.md +441 -0
- package/packages/stack-agents/go/references/standard-stdlib-now.md +181 -0
- package/packages/stack-agents/go/references/stay-updated.md +147 -0
- package/packages/stack-agents/go/references/structs-interfaces.md +312 -0
- package/packages/stack-agents/go/references/sync-primitives.md +395 -0
- package/packages/stack-agents/go/references/testing-patterns.md +483 -0
- package/packages/stack-agents/go/references/troubleshooting.md +98 -0
- package/packages/stack-agents/python/py-reviewer.md +194 -0
- package/packages/stack-agents/rust/rs-reviewer.md +188 -0
- package/packages/stack-agents/typescript/ts-reviewer.md +135 -0
- package/packages/stack-hooks/go/go-context-check-on-edit.sh +56 -0
- package/packages/stack-hooks/go/go-error-check-on-edit.sh +54 -0
- package/packages/stack-hooks/go/go-format-on-edit.sh +32 -0
- package/packages/stack-hooks/go/go-safety-check-on-edit.sh +54 -0
- package/packages/stack-hooks/go/go-vet-on-edit.sh +61 -0
- package/packages/stack-hooks/go/golangci-lint-on-edit.sh +54 -0
- package/packages/stack-hooks/python/ruff-on-edit.sh +67 -0
- package/packages/stack-hooks/rust/cargo-check-on-edit.sh +64 -0
- package/packages/stack-hooks/typescript/typecheck-on-edit.sh +76 -0
- package/packages/stack-rules/go/go-conventions.md +44 -0
- package/packages/stack-rules/go/go-safety.md +35 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Error Wrapping
|
|
2
|
+
|
|
3
|
+
> Reference document for go-error-reviewer. Loaded on demand via Read tool.
|
|
4
|
+
|
|
5
|
+
## The fmt.Errorf %w Pattern
|
|
6
|
+
|
|
7
|
+
The primary mechanism for adding context to errors while preserving the error chain.
|
|
8
|
+
|
|
9
|
+
```go
|
|
10
|
+
func (r *UserRepository) FindByID(ctx context.Context, id int64) (*User, error) {
|
|
11
|
+
row := r.db.QueryRow(ctx, "SELECT ... WHERE id = $1", id)
|
|
12
|
+
var u User
|
|
13
|
+
if err := row.Scan(&u.ID, &u.Name, &u.Email); err != nil {
|
|
14
|
+
if errors.Is(err, pgx.ErrNoRows) {
|
|
15
|
+
return nil, fmt.Errorf("UserRepository.FindByID: %w", domain.ErrNotFound)
|
|
16
|
+
}
|
|
17
|
+
return nil, fmt.Errorf("UserRepository.FindByID: %w", err)
|
|
18
|
+
}
|
|
19
|
+
return &u, nil
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Wrapping Depth Rule
|
|
24
|
+
|
|
25
|
+
**One wrap per function boundary.** Each function that propagates an error adds exactly one layer of context.
|
|
26
|
+
|
|
27
|
+
```go
|
|
28
|
+
// Repository layer
|
|
29
|
+
func (r *Repo) Save(ctx context.Context, u *User) error {
|
|
30
|
+
_, err := r.db.Exec(ctx, query, u.ID, u.Name)
|
|
31
|
+
if err != nil {
|
|
32
|
+
return fmt.Errorf("Repo.Save: %w", err) // ONE wrap
|
|
33
|
+
}
|
|
34
|
+
return nil
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Service layer
|
|
38
|
+
func (s *Service) CreateUser(ctx context.Context, u *User) error {
|
|
39
|
+
if err := s.repo.Save(ctx, u); err != nil {
|
|
40
|
+
return fmt.Errorf("Service.CreateUser: %w", err) // ONE wrap
|
|
41
|
+
}
|
|
42
|
+
return nil
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Handler layer
|
|
46
|
+
func (h *Handler) HandleCreate(w http.ResponseWriter, r *http.Request) {
|
|
47
|
+
if err := h.svc.CreateUser(r.Context(), user); err != nil {
|
|
48
|
+
// Log the full chain, respond with appropriate status
|
|
49
|
+
h.logger.Error("create user failed", "error", err)
|
|
50
|
+
// err message: "Service.CreateUser: Repo.Save: connection refused"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Context Format
|
|
56
|
+
|
|
57
|
+
The standard wrapping format is `TypeName.MethodName: %w`.
|
|
58
|
+
|
|
59
|
+
```go
|
|
60
|
+
// CORRECT — clear provenance
|
|
61
|
+
return fmt.Errorf("UserRepository.FindByID: %w", err)
|
|
62
|
+
return fmt.Errorf("OrderService.PlaceOrder: %w", err)
|
|
63
|
+
return fmt.Errorf("AuthMiddleware.Validate: %w", err)
|
|
64
|
+
|
|
65
|
+
// ALSO ACCEPTABLE — shorter, for internal helpers
|
|
66
|
+
return fmt.Errorf("findByID: %w", err)
|
|
67
|
+
return fmt.Errorf("validate email: %w", err)
|
|
68
|
+
|
|
69
|
+
// WRONG — redundant "error" / "failed"
|
|
70
|
+
return fmt.Errorf("error in UserRepository.FindByID: %w", err) // "error" redundant
|
|
71
|
+
return fmt.Errorf("failed to find user by ID: %w", err) // "failed to" noise
|
|
72
|
+
return fmt.Errorf("UserRepository: FindByID: %w", err) // use dot, not colon
|
|
73
|
+
|
|
74
|
+
// WRONG — no context at all
|
|
75
|
+
return fmt.Errorf("%w", err) // pointless wrap, just return err
|
|
76
|
+
return err // OK if same package, no context needed
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## When to Wrap vs Return As-Is
|
|
80
|
+
|
|
81
|
+
**Decision table:**
|
|
82
|
+
|
|
83
|
+
| Situation | Action | Why |
|
|
84
|
+
|-----------|--------|-----|
|
|
85
|
+
| Crossing package boundary | Wrap | Caller needs to know where error originated |
|
|
86
|
+
| Crossing layer boundary (repo→service) | Wrap | Each layer adds its context |
|
|
87
|
+
| Within same package, different func | Optional | Wrap if func name adds useful context |
|
|
88
|
+
| Within same function, sequential ops | Don't re-wrap | Already at the right context level |
|
|
89
|
+
| Returning sentinel directly | Don't wrap | `return ErrNotFound` is clear enough |
|
|
90
|
+
| Returning sentinel with context | Wrap | `fmt.Errorf("user %d: %w", id, ErrNotFound)` |
|
|
91
|
+
|
|
92
|
+
```go
|
|
93
|
+
// SAME PACKAGE — wrapping is optional
|
|
94
|
+
func (s *Service) process(ctx context.Context, data []byte) error {
|
|
95
|
+
parsed, err := s.parse(data)
|
|
96
|
+
if err != nil {
|
|
97
|
+
return err // same package, parse already has context
|
|
98
|
+
}
|
|
99
|
+
return s.store(ctx, parsed)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// CROSSING BOUNDARY — always wrap
|
|
103
|
+
func (h *Handler) Process(w http.ResponseWriter, r *http.Request) {
|
|
104
|
+
if err := h.svc.Process(r.Context(), body); err != nil {
|
|
105
|
+
return fmt.Errorf("Handler.Process: %w", err)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Chain Inspection
|
|
111
|
+
|
|
112
|
+
### errors.Unwrap
|
|
113
|
+
|
|
114
|
+
Manually traverses one level of the error chain.
|
|
115
|
+
|
|
116
|
+
```go
|
|
117
|
+
err := fmt.Errorf("outer: %w", fmt.Errorf("inner: %w", io.EOF))
|
|
118
|
+
|
|
119
|
+
unwrapped := errors.Unwrap(err)
|
|
120
|
+
// unwrapped.Error() == "inner: end of file"
|
|
121
|
+
|
|
122
|
+
unwrapped2 := errors.Unwrap(unwrapped)
|
|
123
|
+
// unwrapped2 == io.EOF
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Multi-Error Unwrap (Go 1.20+)
|
|
127
|
+
|
|
128
|
+
Errors created with `errors.Join` implement `Unwrap() []error`.
|
|
129
|
+
|
|
130
|
+
```go
|
|
131
|
+
err := errors.Join(
|
|
132
|
+
fmt.Errorf("step 1: %w", ErrNotFound),
|
|
133
|
+
fmt.Errorf("step 2: %w", ErrTimeout),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
// errors.Is checks ALL errors in the tree
|
|
137
|
+
errors.Is(err, ErrNotFound) // true
|
|
138
|
+
errors.Is(err, ErrTimeout) // true
|
|
139
|
+
|
|
140
|
+
// Custom multi-error
|
|
141
|
+
type MultiError struct {
|
|
142
|
+
Errors []error
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
func (e *MultiError) Error() string { ... }
|
|
146
|
+
|
|
147
|
+
func (e *MultiError) Unwrap() []error {
|
|
148
|
+
return e.Errors
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## %w vs %v
|
|
153
|
+
|
|
154
|
+
| Verb | Chain preserved? | Caller can inspect? | Use when |
|
|
155
|
+
|------|-----------------|--------------------|----|
|
|
156
|
+
| `%w` | Yes | Yes — `errors.Is`/`errors.As` work | Default choice — expose error chain |
|
|
157
|
+
| `%v` | No | No — chain is broken | Intentionally hiding implementation details |
|
|
158
|
+
|
|
159
|
+
```go
|
|
160
|
+
// %w — preserves chain (default choice)
|
|
161
|
+
return fmt.Errorf("UserService.Create: %w", err)
|
|
162
|
+
// Caller can: errors.Is(err, pgx.ErrNoRows) → true
|
|
163
|
+
|
|
164
|
+
// %v — breaks chain (intentional hiding)
|
|
165
|
+
return fmt.Errorf("external API error: %v", err)
|
|
166
|
+
// Caller cannot inspect the original error
|
|
167
|
+
// Use when: internal implementation detail should not leak
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**When to use %v (break chain):**
|
|
171
|
+
- External API errors that should be opaque to callers
|
|
172
|
+
- Implementation details that may change (DB driver errors in a service layer)
|
|
173
|
+
- Security-sensitive errors (don't expose internal error messages to HTTP responses)
|
|
174
|
+
|
|
175
|
+
## Anti-Patterns
|
|
176
|
+
|
|
177
|
+
### Double Wrapping
|
|
178
|
+
|
|
179
|
+
```go
|
|
180
|
+
// WRONG — redundant context
|
|
181
|
+
func (s *Service) Get(ctx context.Context, id int64) (*User, error) {
|
|
182
|
+
user, err := s.repo.FindByID(ctx, id)
|
|
183
|
+
if err != nil {
|
|
184
|
+
return nil, fmt.Errorf("Service.Get: failed to get user: %w", err)
|
|
185
|
+
// ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
|
|
186
|
+
// context redundant "failed to get user"
|
|
187
|
+
}
|
|
188
|
+
return user, nil
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// CORRECT
|
|
192
|
+
return nil, fmt.Errorf("Service.Get: %w", err)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Wrapping Then Checking
|
|
196
|
+
|
|
197
|
+
```go
|
|
198
|
+
// WRONG — wrap then check loses the type info
|
|
199
|
+
wrapped := fmt.Errorf("context: %w", err)
|
|
200
|
+
if wrapped == ErrNotFound { // always false for wrapped errors
|
|
201
|
+
// ...
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// CORRECT — check before wrapping, or use errors.Is on wrapped
|
|
205
|
+
if errors.Is(err, ErrNotFound) {
|
|
206
|
+
return nil, fmt.Errorf("Service.Get: %w", domain.ErrNotFound)
|
|
207
|
+
}
|
|
208
|
+
return nil, fmt.Errorf("Service.Get: %w", err)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### String Comparison
|
|
212
|
+
|
|
213
|
+
```go
|
|
214
|
+
// WRONG — fragile, breaks on wrapping
|
|
215
|
+
if err.Error() == "not found" {
|
|
216
|
+
// ...
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// CORRECT
|
|
220
|
+
if errors.Is(err, ErrNotFound) {
|
|
221
|
+
// ...
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## When to Use
|
|
226
|
+
|
|
227
|
+
Apply when reviewing error propagation through function and package boundaries. Flag violations as:
|
|
228
|
+
- **CRITICAL**: Using `%v` where `%w` is needed (breaks caller inspection), comparing error strings
|
|
229
|
+
- **WARNING**: Missing context on wrap, double-wrapping with redundant messages, wrapping at every call within same function
|
|
230
|
+
- **SUGGESTION**: Could simplify by returning unwrapped within same package, inconsistent context format
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Driving gopls (semantic navigation)
|
|
2
|
+
|
|
3
|
+
> Reference document for go-reviewer, go-error-reviewer, go-concurrency-reviewer. Loaded on demand via Read tool. Opt-in — requires the gopls MCP server.
|
|
4
|
+
> Upstream: https://github.com/golang/tools/tree/master/gopls · https://github.com/samber/cc-skills-golang
|
|
5
|
+
|
|
6
|
+
Grep finds text. gopls resolves the build: accurate references, interface satisfaction, and reference blast-radius that a regex cannot compute. This is exactly the accuracy gap on interface- and blast-radius findings. gopls is the Go team's language server; it exposes eight `go_*` tools over MCP. It is an external binary plus a registration step, so treat it as strictly opt-in — never assume it is wired.
|
|
7
|
+
|
|
8
|
+
## Setup (opt-in)
|
|
9
|
+
|
|
10
|
+
**Requires:** the `gopls` MCP server. This capability is inert until it is registered.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
go install golang.org/x/tools/gopls@latest # the binary
|
|
14
|
+
claude mcp add gopls -- gopls mcp # register it with Claude Code
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The server MUST be registered under the exact name `gopls`. The tools resolve as `mcp__gopls__go_*`; that namespace only exists if the server name is `gopls`. Register it under any other name and none of the tools below are callable.
|
|
18
|
+
|
|
19
|
+
If the server is not registered, everything below is inert — fall back to grep for references and `go doc` for external symbol contracts, exactly as the Evidence Gate already prescribes. Never assume the server is wired: probe with `go_workspace` first, and if it errors, drop to the grep + `go doc` path for the whole review.
|
|
20
|
+
|
|
21
|
+
## The eight tools
|
|
22
|
+
|
|
23
|
+
| Tool | Params | Use for |
|
|
24
|
+
|------|--------|---------|
|
|
25
|
+
| `go_workspace` | `{}` | Modules, packages, and layout of the workspace. Probe first — a clean return means the server is live. |
|
|
26
|
+
| `go_search` | `{"query"}` | Fuzzy-find a symbol by name, workspace-wide. Locate a definition before reading it. |
|
|
27
|
+
| `go_file_context` | `{"file"}` | A file's own symbols plus its intra-package dependencies. The semantic replacement for reading a file to orient. |
|
|
28
|
+
| `go_package_api` | `{"packagePaths":[]}` | A package's exported API surface. Resolves interface satisfaction and public contracts. |
|
|
29
|
+
| `go_symbol_references` | `{"file","symbol"}` | Every reference to a symbol, workspace-wide. The blast-radius tool — run before touching any definition. |
|
|
30
|
+
| `go_diagnostics` | `{"files":[]}` | Compiler + analyzer diagnostics for the listed files. NOT pushed automatically — you must call it after every edit. |
|
|
31
|
+
| `go_vulncheck` | `{"pattern":"./..."}` | Reachable vulnerabilities via govulncheck. Run only when `go.mod` / `go.sum` changed. |
|
|
32
|
+
| `go_rename_symbol` | *(params inferred: `file`, `symbol`, `newName` — confirm against your running server; upstream mcp.md/matrix.md do not publish the signature)* | Rename a symbol and every reference to it. Blocks any rename that would break interface satisfaction. |
|
|
33
|
+
|
|
34
|
+
## Read → Edit workflow order
|
|
35
|
+
|
|
36
|
+
**Reading (orient before you judge):**
|
|
37
|
+
`go_workspace` (probe + layout) → `go_search` (find the symbol) → `go_file_context` (the file's own symbols + local deps) → `go_package_api` (the exported surface / interface contracts).
|
|
38
|
+
|
|
39
|
+
**Editing (blast-radius before you touch, verify after):**
|
|
40
|
+
1. `go_symbol_references` on the definition to establish the full blast radius **BEFORE** you change anything. Every caller you would break is here.
|
|
41
|
+
2. Make the edit.
|
|
42
|
+
3. `go_diagnostics` on the changed files — **MANDATORY**. Diagnostics are not pushed to you; an unrun diagnostic is an unverified edit.
|
|
43
|
+
4. Fix what it reports and re-run `go_diagnostics` until clean.
|
|
44
|
+
5. `go_vulncheck` **only if `go.mod` changed** — a new or bumped dependency can pull in a reachable CVE.
|
|
45
|
+
|
|
46
|
+
## Load-bearing gotchas
|
|
47
|
+
|
|
48
|
+
- **Disk-only, no unsaved buffer.** The server sees files as they are on disk. Write your edit before you query — an unsaved change is invisible to `go_symbol_references`, `go_diagnostics`, and every other tool.
|
|
49
|
+
- **Results reflect ONLY the queried file's build config.** A query on `foo_windows.go` resolves under the Windows build; it will miss references in `bar_linux.go`. For cross-platform blast radius, re-run the reference query under the other GOOS / build-tag combinations — do not trust a single-config answer as complete.
|
|
50
|
+
- **References and call-hierarchy are static-dispatch only.** Calls made through an interface method or a function value are INVISIBLE to `go_symbol_references` — it resolves concrete, statically-dispatched calls, not dynamic ones. When a symbol is reached via an interface or a `func` field, corroborate the call sites manually (grep the method name, trace the interface) before you trust the reported blast radius.
|
|
51
|
+
- **Rename blocks interface-breaking changes.** `go_rename_symbol` refuses a rename that would break interface satisfaction — a rejection is signal, not a bug. It tells you the symbol participates in an interface contract you had not accounted for.
|
|
52
|
+
- **Scope = workspace + go.sum-pinned deps.** The server sees your workspace and the dependencies pinned in `go.sum`. A package that is not yet imported is out of scope; `go_search` / `go_package_api` will not find it. For a symbol in a not-yet-imported package, fall back to `go doc <pkg>` per the Evidence Gate instead of concluding it does not exist.
|
|
53
|
+
|
|
54
|
+
## Access-path ranking
|
|
55
|
+
|
|
56
|
+
Prefer the MCP path. It is the production interface.
|
|
57
|
+
|
|
58
|
+
1. **MCP (preferred)** — the `go_*` tools above. This is the path these agents use.
|
|
59
|
+
2. **Native LSP (bonus, experimental)** — enable with `ENABLE_LSP_TOOL=1`. Auto-pushes diagnostics as you edit, but it is experimental; use it only as a convenience layer over MCP, never as the sole path.
|
|
60
|
+
3. **CLI (`gopls codeaction`, etc.)** — debugging-only. Reach for the `gopls` command line to diagnose the server itself, never as the production navigation path.
|
|
61
|
+
|
|
62
|
+
Whatever the path, a gopls result is a valid VERIFIED citation — the same standing as a `file:line` you Read. When gopls is not wired, none of this applies: grep and `go doc` remain the citation sources. Never assume the server is wired; confirm it, then use it.
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# Goroutine Lifecycle
|
|
2
|
+
|
|
3
|
+
> Reference document for go-concurrency-reviewer. Loaded on demand via Read tool.
|
|
4
|
+
|
|
5
|
+
## Spawn Patterns
|
|
6
|
+
|
|
7
|
+
Always spawn goroutines with a clear exit path. Never fire-and-forget.
|
|
8
|
+
|
|
9
|
+
```go
|
|
10
|
+
// CORRECT — goroutine with context cancellation
|
|
11
|
+
func (s *Service) StartWorker(ctx context.Context) {
|
|
12
|
+
go func() {
|
|
13
|
+
for {
|
|
14
|
+
select {
|
|
15
|
+
case <-ctx.Done():
|
|
16
|
+
return // clean exit
|
|
17
|
+
case job := <-s.jobs:
|
|
18
|
+
s.process(ctx, job)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// CORRECT — goroutine with WaitGroup for lifecycle tracking
|
|
25
|
+
func (s *Service) StartWorkers(ctx context.Context, n int) {
|
|
26
|
+
s.wg.Add(n)
|
|
27
|
+
for range n {
|
|
28
|
+
go func() {
|
|
29
|
+
defer s.wg.Done()
|
|
30
|
+
for {
|
|
31
|
+
select {
|
|
32
|
+
case <-ctx.Done():
|
|
33
|
+
return
|
|
34
|
+
case job := <-s.jobs:
|
|
35
|
+
s.process(ctx, job)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// WRONG — fire and forget, no exit path
|
|
43
|
+
func (s *Service) StartWorker() {
|
|
44
|
+
go func() {
|
|
45
|
+
for job := range s.jobs { // blocks forever if channel never closed
|
|
46
|
+
s.process(context.Background(), job)
|
|
47
|
+
}
|
|
48
|
+
}()
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Shutdown Mechanisms
|
|
53
|
+
|
|
54
|
+
### Context Cancellation (preferred)
|
|
55
|
+
|
|
56
|
+
```go
|
|
57
|
+
ctx, cancel := context.WithCancel(context.Background())
|
|
58
|
+
defer cancel() // cancels all child goroutines
|
|
59
|
+
|
|
60
|
+
go worker(ctx)
|
|
61
|
+
go worker(ctx)
|
|
62
|
+
|
|
63
|
+
// Later: cancel() signals all workers to stop
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Done Channel
|
|
67
|
+
|
|
68
|
+
```go
|
|
69
|
+
type Server struct {
|
|
70
|
+
done chan struct{}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func (s *Server) Start() {
|
|
74
|
+
s.done = make(chan struct{})
|
|
75
|
+
go s.run()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
func (s *Server) Stop() {
|
|
79
|
+
close(s.done) // signal all goroutines
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
func (s *Server) run() {
|
|
83
|
+
ticker := time.NewTicker(time.Second)
|
|
84
|
+
defer ticker.Stop()
|
|
85
|
+
for {
|
|
86
|
+
select {
|
|
87
|
+
case <-s.done:
|
|
88
|
+
return
|
|
89
|
+
case <-ticker.C:
|
|
90
|
+
s.tick()
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Signal-Based Shutdown
|
|
97
|
+
|
|
98
|
+
```go
|
|
99
|
+
func main() {
|
|
100
|
+
ctx, stop := signal.NotifyContext(context.Background(),
|
|
101
|
+
syscall.SIGINT, syscall.SIGTERM)
|
|
102
|
+
defer stop()
|
|
103
|
+
|
|
104
|
+
srv := NewServer()
|
|
105
|
+
go func() {
|
|
106
|
+
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
107
|
+
log.Fatal("listen", "error", err)
|
|
108
|
+
}
|
|
109
|
+
}()
|
|
110
|
+
|
|
111
|
+
<-ctx.Done() // block until signal
|
|
112
|
+
log.Info("shutting down")
|
|
113
|
+
|
|
114
|
+
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
115
|
+
defer cancel()
|
|
116
|
+
|
|
117
|
+
if err := srv.Shutdown(shutdownCtx); err != nil {
|
|
118
|
+
log.Error("shutdown", "error", err)
|
|
119
|
+
}
|
|
120
|
+
log.Info("shutdown complete")
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Graceful Shutdown Pattern
|
|
125
|
+
|
|
126
|
+
Full production-ready graceful shutdown with multiple components:
|
|
127
|
+
|
|
128
|
+
```go
|
|
129
|
+
func run(ctx context.Context) error {
|
|
130
|
+
ctx, cancel := context.WithCancel(ctx)
|
|
131
|
+
defer cancel()
|
|
132
|
+
|
|
133
|
+
g, ctx := errgroup.WithContext(ctx)
|
|
134
|
+
|
|
135
|
+
// HTTP server
|
|
136
|
+
srv := &http.Server{Addr: ":8080", Handler: mux}
|
|
137
|
+
g.Go(func() error {
|
|
138
|
+
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
|
|
139
|
+
return err
|
|
140
|
+
}
|
|
141
|
+
return nil
|
|
142
|
+
})
|
|
143
|
+
g.Go(func() error {
|
|
144
|
+
<-ctx.Done()
|
|
145
|
+
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
146
|
+
defer cancel()
|
|
147
|
+
return srv.Shutdown(shutdownCtx)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
// Background worker
|
|
151
|
+
g.Go(func() error {
|
|
152
|
+
return worker.Run(ctx) // must respect ctx.Done()
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// Metrics flusher
|
|
156
|
+
g.Go(func() error {
|
|
157
|
+
<-ctx.Done()
|
|
158
|
+
return metrics.Flush(context.Background())
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
return g.Wait()
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Leak Prevention
|
|
166
|
+
|
|
167
|
+
Every goroutine must have a guaranteed exit path. Common leak patterns:
|
|
168
|
+
|
|
169
|
+
### Leak: Blocked Channel Read
|
|
170
|
+
|
|
171
|
+
```go
|
|
172
|
+
// LEAK — ch may never receive a value
|
|
173
|
+
func leaky() {
|
|
174
|
+
ch := make(chan int)
|
|
175
|
+
go func() {
|
|
176
|
+
val := <-ch // blocks forever if nobody sends
|
|
177
|
+
process(val)
|
|
178
|
+
}()
|
|
179
|
+
// function returns, ch goes out of scope, goroutine leaks
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// FIXED — use context or done channel
|
|
183
|
+
func fixed(ctx context.Context) {
|
|
184
|
+
ch := make(chan int)
|
|
185
|
+
go func() {
|
|
186
|
+
select {
|
|
187
|
+
case val := <-ch:
|
|
188
|
+
process(val)
|
|
189
|
+
case <-ctx.Done():
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
}()
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Leak: Blocked Channel Write
|
|
197
|
+
|
|
198
|
+
```go
|
|
199
|
+
// LEAK — unbuffered channel, nobody reads
|
|
200
|
+
func leaky() {
|
|
201
|
+
ch := make(chan result)
|
|
202
|
+
go func() {
|
|
203
|
+
ch <- doWork() // blocks forever if nobody reads
|
|
204
|
+
}()
|
|
205
|
+
// function returns without reading from ch
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// FIXED — buffered channel of 1
|
|
209
|
+
func fixed() {
|
|
210
|
+
ch := make(chan result, 1) // won't block even if nobody reads
|
|
211
|
+
go func() {
|
|
212
|
+
ch <- doWork()
|
|
213
|
+
}()
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Leak: Forgotten Ticker
|
|
218
|
+
|
|
219
|
+
```go
|
|
220
|
+
// LEAK — ticker not stopped
|
|
221
|
+
func leaky() {
|
|
222
|
+
ticker := time.NewTicker(time.Second)
|
|
223
|
+
go func() {
|
|
224
|
+
for range ticker.C {
|
|
225
|
+
// ...
|
|
226
|
+
}
|
|
227
|
+
}()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// FIXED — stop ticker on exit
|
|
231
|
+
func fixed(ctx context.Context) {
|
|
232
|
+
ticker := time.NewTicker(time.Second)
|
|
233
|
+
go func() {
|
|
234
|
+
defer ticker.Stop()
|
|
235
|
+
for {
|
|
236
|
+
select {
|
|
237
|
+
case <-ctx.Done():
|
|
238
|
+
return
|
|
239
|
+
case <-ticker.C:
|
|
240
|
+
// ...
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}()
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## goleak Integration
|
|
248
|
+
|
|
249
|
+
Detect goroutine leaks in tests.
|
|
250
|
+
|
|
251
|
+
```go
|
|
252
|
+
import "go.uber.org/goleak"
|
|
253
|
+
|
|
254
|
+
// Option 1: TestMain — checks all tests in package
|
|
255
|
+
func TestMain(m *testing.M) {
|
|
256
|
+
goleak.VerifyTestMain(m)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Option 2: Per-test — more granular
|
|
260
|
+
func TestNoLeak(t *testing.T) {
|
|
261
|
+
defer goleak.VerifyNone(t)
|
|
262
|
+
// test code...
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Option 3: Ignore known goroutines
|
|
266
|
+
func TestMain(m *testing.M) {
|
|
267
|
+
goleak.VerifyTestMain(m,
|
|
268
|
+
goleak.IgnoreTopFunction("database/sql.(*DB).connectionOpener"),
|
|
269
|
+
goleak.IgnoreAnyFunction("go.opencensus.io/stats/view.(*worker).start"),
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Worker Pool Pattern
|
|
275
|
+
|
|
276
|
+
Bounded concurrency using a semaphore channel.
|
|
277
|
+
|
|
278
|
+
```go
|
|
279
|
+
func ProcessAll(ctx context.Context, items []Item, maxWorkers int) error {
|
|
280
|
+
g, ctx := errgroup.WithContext(ctx)
|
|
281
|
+
g.SetLimit(maxWorkers) // errgroup built-in limiter (Go 1.20+)
|
|
282
|
+
|
|
283
|
+
for _, item := range items {
|
|
284
|
+
g.Go(func() error {
|
|
285
|
+
return process(ctx, item)
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return g.Wait()
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Manual semaphore pattern (pre-errgroup.SetLimit)
|
|
293
|
+
func ProcessAll(ctx context.Context, items []Item, maxWorkers int) error {
|
|
294
|
+
sem := make(chan struct{}, maxWorkers)
|
|
295
|
+
g, ctx := errgroup.WithContext(ctx)
|
|
296
|
+
|
|
297
|
+
for _, item := range items {
|
|
298
|
+
sem <- struct{}{} // acquire
|
|
299
|
+
g.Go(func() error {
|
|
300
|
+
defer func() { <-sem }() // release
|
|
301
|
+
return process(ctx, item)
|
|
302
|
+
})
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return g.Wait()
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Error Propagation with errgroup
|
|
310
|
+
|
|
311
|
+
```go
|
|
312
|
+
func FetchAll(ctx context.Context, urls []string) ([]Response, error) {
|
|
313
|
+
results := make([]Response, len(urls))
|
|
314
|
+
g, ctx := errgroup.WithContext(ctx)
|
|
315
|
+
|
|
316
|
+
for i, url := range urls {
|
|
317
|
+
g.Go(func() error {
|
|
318
|
+
resp, err := fetch(ctx, url)
|
|
319
|
+
if err != nil {
|
|
320
|
+
return fmt.Errorf("fetch %s: %w", url, err)
|
|
321
|
+
}
|
|
322
|
+
results[i] = resp // safe: each goroutine writes to unique index
|
|
323
|
+
return nil
|
|
324
|
+
})
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if err := g.Wait(); err != nil {
|
|
328
|
+
return nil, err // returns first error, cancels remaining via ctx
|
|
329
|
+
}
|
|
330
|
+
return results, nil
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Decision Table: Goroutine Lifecycle Tools
|
|
335
|
+
|
|
336
|
+
| Need | Tool | Why |
|
|
337
|
+
|------|------|-----|
|
|
338
|
+
| Wait for N goroutines | `sync.WaitGroup` | Simple count-based waiting |
|
|
339
|
+
| Wait + first error cancels all | `errgroup.Group` | Combines WaitGroup + context cancel |
|
|
340
|
+
| Bounded parallelism | `errgroup.SetLimit(n)` | Limits concurrent goroutines |
|
|
341
|
+
| Background worker with shutdown | `context.WithCancel` | Parent controls lifecycle |
|
|
342
|
+
| Periodic task | `time.Ticker` + select | Cancelable periodic execution |
|
|
343
|
+
| OS signal handling | `signal.NotifyContext` | Converts signal to context cancel |
|
|
344
|
+
| HTTP server shutdown | `srv.Shutdown(ctx)` | Drains in-flight requests |
|
|
345
|
+
|
|
346
|
+
## When to Use
|
|
347
|
+
|
|
348
|
+
Apply when reviewing goroutine creation, lifecycle management, and shutdown patterns. Flag violations as:
|
|
349
|
+
- **CRITICAL**: Goroutine without exit path (leak), fire-and-forget with no cancellation, missing WaitGroup/errgroup
|
|
350
|
+
- **WARNING**: No graceful shutdown for long-running services, ticker without Stop, blocked channel without timeout
|
|
351
|
+
- **SUGGESTION**: Could use errgroup.SetLimit instead of manual semaphore, missing goleak in tests
|