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,251 @@
|
|
|
1
|
+
# Error Creation
|
|
2
|
+
|
|
3
|
+
> Reference document for go-error-reviewer. Loaded on demand via Read tool.
|
|
4
|
+
|
|
5
|
+
## Sentinel Errors
|
|
6
|
+
|
|
7
|
+
Package-level variables representing specific error conditions. Always defined with `errors.New`.
|
|
8
|
+
|
|
9
|
+
```go
|
|
10
|
+
package user
|
|
11
|
+
|
|
12
|
+
import "errors"
|
|
13
|
+
|
|
14
|
+
var (
|
|
15
|
+
ErrNotFound = errors.New("user not found")
|
|
16
|
+
ErrAlreadyExists = errors.New("user already exists")
|
|
17
|
+
ErrInvalidInput = errors.New("invalid input")
|
|
18
|
+
ErrForbidden = errors.New("forbidden")
|
|
19
|
+
ErrInactive = errors.New("user is inactive")
|
|
20
|
+
)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Naming rules:**
|
|
24
|
+
- Prefix with `Err`
|
|
25
|
+
- Exported (capital E) if used outside package
|
|
26
|
+
- Descriptive but concise: `ErrNotFound` not `ErrTheUserWasNotFound`
|
|
27
|
+
- Message: lowercase, no punctuation, no "failed to" prefix
|
|
28
|
+
|
|
29
|
+
**When to use sentinels:**
|
|
30
|
+
- Caller needs to check for specific condition with `errors.Is`
|
|
31
|
+
- Condition is a well-known domain concept
|
|
32
|
+
- No additional context needed beyond "this happened"
|
|
33
|
+
|
|
34
|
+
## Custom Error Types
|
|
35
|
+
|
|
36
|
+
Implement the `error` interface when errors carry structured data.
|
|
37
|
+
|
|
38
|
+
```go
|
|
39
|
+
type ValidationError struct {
|
|
40
|
+
Field string
|
|
41
|
+
Message string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func (e *ValidationError) Error() string {
|
|
45
|
+
return fmt.Sprintf("validation: %s — %s", e.Field, e.Message)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Usage
|
|
49
|
+
func ValidateEmail(email string) error {
|
|
50
|
+
if !strings.Contains(email, "@") {
|
|
51
|
+
return &ValidationError{
|
|
52
|
+
Field: "email",
|
|
53
|
+
Message: "must contain @",
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return nil
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Caller inspects with errors.As
|
|
60
|
+
var verr *ValidationError
|
|
61
|
+
if errors.As(err, &verr) {
|
|
62
|
+
log.Warn("field failed validation",
|
|
63
|
+
"field", verr.Field,
|
|
64
|
+
"message", verr.Message,
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Multi-Field Validation Error
|
|
70
|
+
|
|
71
|
+
```go
|
|
72
|
+
type ValidationErrors struct {
|
|
73
|
+
Errors []ValidationError
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
func (e *ValidationErrors) Error() string {
|
|
77
|
+
msgs := make([]string, len(e.Errors))
|
|
78
|
+
for i, ve := range e.Errors {
|
|
79
|
+
msgs[i] = ve.Error()
|
|
80
|
+
}
|
|
81
|
+
return strings.Join(msgs, "; ")
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
func (e *ValidationErrors) HasErrors() bool {
|
|
85
|
+
return len(e.Errors) > 0
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
func (e *ValidationErrors) Add(field, message string) {
|
|
89
|
+
e.Errors = append(e.Errors, ValidationError{Field: field, Message: message})
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Rich Domain Error
|
|
94
|
+
|
|
95
|
+
```go
|
|
96
|
+
type DomainError struct {
|
|
97
|
+
Code string // machine-readable: "USER_NOT_FOUND"
|
|
98
|
+
Message string // human-readable
|
|
99
|
+
Err error // wrapped cause
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
func (e *DomainError) Error() string {
|
|
103
|
+
if e.Err != nil {
|
|
104
|
+
return fmt.Sprintf("%s: %s: %v", e.Code, e.Message, e.Err)
|
|
105
|
+
}
|
|
106
|
+
return fmt.Sprintf("%s: %s", e.Code, e.Message)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
func (e *DomainError) Unwrap() error {
|
|
110
|
+
return e.Err
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## errors.Join (Go 1.20+)
|
|
115
|
+
|
|
116
|
+
Combine multiple errors into one. Useful for batch operations and multi-step validation.
|
|
117
|
+
|
|
118
|
+
```go
|
|
119
|
+
func ValidateUser(u *User) error {
|
|
120
|
+
var errs []error
|
|
121
|
+
|
|
122
|
+
if u.Name == "" {
|
|
123
|
+
errs = append(errs, fmt.Errorf("name is required"))
|
|
124
|
+
}
|
|
125
|
+
if u.Email == "" {
|
|
126
|
+
errs = append(errs, fmt.Errorf("email is required"))
|
|
127
|
+
}
|
|
128
|
+
if u.Age < 0 {
|
|
129
|
+
errs = append(errs, fmt.Errorf("age must be non-negative"))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return errors.Join(errs...) // nil if errs is empty
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Batch operation
|
|
136
|
+
func DeleteUsers(ctx context.Context, ids []int64) error {
|
|
137
|
+
var errs []error
|
|
138
|
+
for _, id := range ids {
|
|
139
|
+
if err := deleteUser(ctx, id); err != nil {
|
|
140
|
+
errs = append(errs, fmt.Errorf("delete user %d: %w", id, err))
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return errors.Join(errs...)
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Key behavior of errors.Join:**
|
|
148
|
+
- Returns `nil` if all errors are nil or the slice is empty
|
|
149
|
+
- The joined error's `Error()` joins messages with `\n`
|
|
150
|
+
- `errors.Is` and `errors.As` check **all** contained errors
|
|
151
|
+
|
|
152
|
+
## Error Message Format
|
|
153
|
+
|
|
154
|
+
| Rule | Good | Bad |
|
|
155
|
+
|------|------|-----|
|
|
156
|
+
| Lowercase start | `"user not found"` | `"User not found"` |
|
|
157
|
+
| No trailing punctuation | `"invalid email"` | `"invalid email."` |
|
|
158
|
+
| No "failed to" prefix | `"parse config: %w"` | `"failed to parse config: %w"` |
|
|
159
|
+
| Context: noun + verb | `"user.Save: %w"` | `"%w"` (no context) |
|
|
160
|
+
| No redundant wrapping | wrap once per boundary | double-wrapping same context |
|
|
161
|
+
|
|
162
|
+
```go
|
|
163
|
+
// CORRECT message format
|
|
164
|
+
return fmt.Errorf("user not found: id=%d", id)
|
|
165
|
+
return fmt.Errorf("parse config: %w", err)
|
|
166
|
+
return fmt.Errorf("UserService.Create: %w", err)
|
|
167
|
+
|
|
168
|
+
// WRONG
|
|
169
|
+
return fmt.Errorf("Failed to find user: %w", err) // capital, "failed to"
|
|
170
|
+
return fmt.Errorf("error finding user: %w.", err) // "error", punctuation
|
|
171
|
+
return errors.New("An error occurred") // vague, capital
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Wrapping vs Creating
|
|
175
|
+
|
|
176
|
+
**Decision table:**
|
|
177
|
+
|
|
178
|
+
| Situation | Action | Example |
|
|
179
|
+
|-----------|--------|---------|
|
|
180
|
+
| Propagating from lower layer | Wrap with `%w` | `fmt.Errorf("Repo.Save: %w", err)` |
|
|
181
|
+
| New condition detected | Create new error | `return ErrInvalidInput` |
|
|
182
|
+
| Adding context to sentinel | Wrap the sentinel | `fmt.Errorf("email field: %w", ErrInvalidInput)` |
|
|
183
|
+
| Multiple errors accumulated | Join | `errors.Join(errs...)` |
|
|
184
|
+
| Error should not be inspected by caller | Use `%v` | `fmt.Errorf("internal: %v", err)` |
|
|
185
|
+
|
|
186
|
+
## Domain Error Vocabulary
|
|
187
|
+
|
|
188
|
+
Standard set of domain errors every service should define:
|
|
189
|
+
|
|
190
|
+
```go
|
|
191
|
+
package domain
|
|
192
|
+
|
|
193
|
+
var (
|
|
194
|
+
// Resource errors
|
|
195
|
+
ErrNotFound = errors.New("not found")
|
|
196
|
+
ErrAlreadyExists = errors.New("already exists")
|
|
197
|
+
ErrConflict = errors.New("conflict") // optimistic lock, version mismatch
|
|
198
|
+
|
|
199
|
+
// Input errors
|
|
200
|
+
ErrInvalidInput = errors.New("invalid input")
|
|
201
|
+
ErrMissingField = errors.New("missing required field")
|
|
202
|
+
|
|
203
|
+
// Auth errors
|
|
204
|
+
ErrUnauthorized = errors.New("unauthorized") // not authenticated
|
|
205
|
+
ErrForbidden = errors.New("forbidden") // authenticated but not allowed
|
|
206
|
+
|
|
207
|
+
// State errors
|
|
208
|
+
ErrInactive = errors.New("inactive")
|
|
209
|
+
ErrExpired = errors.New("expired")
|
|
210
|
+
ErrRateLimited = errors.New("rate limited")
|
|
211
|
+
|
|
212
|
+
// System errors
|
|
213
|
+
ErrTimeout = errors.New("timeout")
|
|
214
|
+
ErrUnavailable = errors.New("service unavailable")
|
|
215
|
+
)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Mapping Domain Errors to HTTP Status
|
|
219
|
+
|
|
220
|
+
```go
|
|
221
|
+
func DomainErrorToHTTPStatus(err error) int {
|
|
222
|
+
switch {
|
|
223
|
+
case errors.Is(err, domain.ErrNotFound):
|
|
224
|
+
return http.StatusNotFound
|
|
225
|
+
case errors.Is(err, domain.ErrAlreadyExists):
|
|
226
|
+
return http.StatusConflict
|
|
227
|
+
case errors.Is(err, domain.ErrConflict):
|
|
228
|
+
return http.StatusConflict
|
|
229
|
+
case errors.Is(err, domain.ErrInvalidInput),
|
|
230
|
+
errors.Is(err, domain.ErrMissingField):
|
|
231
|
+
return http.StatusBadRequest
|
|
232
|
+
case errors.Is(err, domain.ErrUnauthorized):
|
|
233
|
+
return http.StatusUnauthorized
|
|
234
|
+
case errors.Is(err, domain.ErrForbidden):
|
|
235
|
+
return http.StatusForbidden
|
|
236
|
+
case errors.Is(err, domain.ErrRateLimited):
|
|
237
|
+
return http.StatusTooManyRequests
|
|
238
|
+
case errors.Is(err, domain.ErrTimeout):
|
|
239
|
+
return http.StatusGatewayTimeout
|
|
240
|
+
default:
|
|
241
|
+
return http.StatusInternalServerError
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## When to Use
|
|
247
|
+
|
|
248
|
+
Apply when reviewing error definitions and creation patterns. Flag violations as:
|
|
249
|
+
- **CRITICAL**: Sentinel errors defined with `fmt.Errorf` (not wrappable), error messages starting with capital letters in chains
|
|
250
|
+
- **WARNING**: Missing domain error vocabulary, creating errors inline instead of sentinels for repeated conditions
|
|
251
|
+
- **SUGGESTION**: Could use `errors.Join` for batch operations, missing structured error type for validation
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# Error Inspection
|
|
2
|
+
|
|
3
|
+
> Reference document for go-error-reviewer. Loaded on demand via Read tool.
|
|
4
|
+
|
|
5
|
+
## errors.Is — Match Sentinels Through Chain
|
|
6
|
+
|
|
7
|
+
Traverses the entire error chain (unwrapping recursively) to find a target error.
|
|
8
|
+
|
|
9
|
+
```go
|
|
10
|
+
// Setup
|
|
11
|
+
var ErrNotFound = errors.New("not found")
|
|
12
|
+
|
|
13
|
+
// Wrap several levels deep
|
|
14
|
+
err := fmt.Errorf("handler: %w",
|
|
15
|
+
fmt.Errorf("service: %w",
|
|
16
|
+
fmt.Errorf("repo: %w", ErrNotFound)))
|
|
17
|
+
|
|
18
|
+
// errors.Is traverses the full chain
|
|
19
|
+
errors.Is(err, ErrNotFound) // true
|
|
20
|
+
errors.Is(err, io.EOF) // false
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Custom Is Method
|
|
24
|
+
|
|
25
|
+
Implement `Is(target error) bool` on your error type for custom matching logic.
|
|
26
|
+
|
|
27
|
+
```go
|
|
28
|
+
type HTTPError struct {
|
|
29
|
+
StatusCode int
|
|
30
|
+
Message string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func (e *HTTPError) Error() string {
|
|
34
|
+
return fmt.Sprintf("HTTP %d: %s", e.StatusCode, e.Message)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Custom Is: match by status code family
|
|
38
|
+
func (e *HTTPError) Is(target error) bool {
|
|
39
|
+
t, ok := target.(*HTTPError)
|
|
40
|
+
if !ok {
|
|
41
|
+
return false
|
|
42
|
+
}
|
|
43
|
+
// Match by status code family (4xx matches any 4xx)
|
|
44
|
+
return e.StatusCode/100 == t.StatusCode/100
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Usage
|
|
48
|
+
err := &HTTPError{StatusCode: 404, Message: "user not found"}
|
|
49
|
+
target := &HTTPError{StatusCode: 400} // any 4xx
|
|
50
|
+
errors.Is(err, target) // true — same family
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## errors.As — Extract Typed Errors
|
|
54
|
+
|
|
55
|
+
Finds the first error in the chain that matches the target type and sets the target pointer.
|
|
56
|
+
|
|
57
|
+
```go
|
|
58
|
+
type ValidationError struct {
|
|
59
|
+
Field string
|
|
60
|
+
Message string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
func (e *ValidationError) Error() string {
|
|
64
|
+
return fmt.Sprintf("validation: %s — %s", e.Field, e.Message)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Wrap the typed error
|
|
68
|
+
err := fmt.Errorf("handler: %w",
|
|
69
|
+
fmt.Errorf("service: %w",
|
|
70
|
+
&ValidationError{Field: "email", Message: "invalid format"}))
|
|
71
|
+
|
|
72
|
+
// Extract through the chain
|
|
73
|
+
var verr *ValidationError
|
|
74
|
+
if errors.As(err, &verr) {
|
|
75
|
+
fmt.Println(verr.Field) // "email"
|
|
76
|
+
fmt.Println(verr.Message) // "invalid format"
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Custom As Method
|
|
81
|
+
|
|
82
|
+
Implement `As(target any) bool` for custom extraction logic.
|
|
83
|
+
|
|
84
|
+
```go
|
|
85
|
+
type DomainError struct {
|
|
86
|
+
Code string
|
|
87
|
+
Message string
|
|
88
|
+
Cause error
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
func (e *DomainError) Error() string {
|
|
92
|
+
return fmt.Sprintf("[%s] %s", e.Code, e.Message)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
func (e *DomainError) Unwrap() error {
|
|
96
|
+
return e.Cause
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Custom As: allow extracting as *DomainError even from subtypes
|
|
100
|
+
func (e *DomainError) As(target any) bool {
|
|
101
|
+
if t, ok := target.(**DomainError); ok {
|
|
102
|
+
*t = e
|
|
103
|
+
return true
|
|
104
|
+
}
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## errors.AsType — Extract Typed Errors (Go 1.26+)
|
|
110
|
+
|
|
111
|
+
Generic form of `errors.As`: returns the extracted error and a bool instead of writing through a pointer. Same chain-traversal semantics — one fewer variable.
|
|
112
|
+
|
|
113
|
+
```go
|
|
114
|
+
// Extract through the chain (Go 1.26+)
|
|
115
|
+
if verr, ok := errors.AsType[*ValidationError](err); ok {
|
|
116
|
+
fmt.Println(verr.Field) // "email"
|
|
117
|
+
fmt.Println(verr.Message) // "invalid format"
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The signature is `func AsType[E error](err error) (E, bool)` — the type parameter is constrained to `error`, so `E` must itself be an error type. Prefer it over `errors.As` for the common single-extraction case: no `var target` declaration, no `&target`, and the result type is inferred at the call site.
|
|
122
|
+
|
|
123
|
+
Keep `errors.As` while you still compile against Go <1.26 (`AsType` didn't exist before), and when you already hold a pointer to write into.
|
|
124
|
+
|
|
125
|
+
## errors.Unwrap — Manual Traversal
|
|
126
|
+
|
|
127
|
+
Returns the next error in the chain. Rarely needed directly — prefer `errors.Is`/`errors.As`.
|
|
128
|
+
|
|
129
|
+
```go
|
|
130
|
+
err := fmt.Errorf("outer: %w", fmt.Errorf("inner: %w", io.EOF))
|
|
131
|
+
|
|
132
|
+
e1 := errors.Unwrap(err) // "inner: EOF"
|
|
133
|
+
e2 := errors.Unwrap(e1) // io.EOF
|
|
134
|
+
e3 := errors.Unwrap(e2) // nil (end of chain)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Multi-Error Inspection (Go 1.20+)
|
|
138
|
+
|
|
139
|
+
Errors from `errors.Join` or types implementing `Unwrap() []error` are traversed by `errors.Is` and `errors.As` across all branches.
|
|
140
|
+
|
|
141
|
+
```go
|
|
142
|
+
err := errors.Join(
|
|
143
|
+
fmt.Errorf("validation: %w", ErrInvalidInput),
|
|
144
|
+
fmt.Errorf("auth: %w", ErrUnauthorized),
|
|
145
|
+
fmt.Errorf("rate: %w", ErrRateLimited),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
// errors.Is checks ALL branches
|
|
149
|
+
errors.Is(err, ErrInvalidInput) // true
|
|
150
|
+
errors.Is(err, ErrUnauthorized) // true
|
|
151
|
+
errors.Is(err, ErrRateLimited) // true
|
|
152
|
+
errors.Is(err, ErrNotFound) // false
|
|
153
|
+
|
|
154
|
+
// errors.As also checks all branches
|
|
155
|
+
var verr *ValidationError
|
|
156
|
+
if errors.As(err, &verr) {
|
|
157
|
+
// found first ValidationError in any branch
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Building Multi-Errors Incrementally
|
|
162
|
+
|
|
163
|
+
```go
|
|
164
|
+
type MultiError struct {
|
|
165
|
+
errs []error
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
func (m *MultiError) Add(err error) {
|
|
169
|
+
if err != nil {
|
|
170
|
+
m.errs = append(m.errs, err)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
func (m *MultiError) Err() error {
|
|
175
|
+
if len(m.errs) == 0 {
|
|
176
|
+
return nil
|
|
177
|
+
}
|
|
178
|
+
return errors.Join(m.errs...)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Usage
|
|
182
|
+
func processBatch(items []Item) error {
|
|
183
|
+
var me MultiError
|
|
184
|
+
for _, item := range items {
|
|
185
|
+
me.Add(processOne(item))
|
|
186
|
+
}
|
|
187
|
+
return me.Err() // nil if no errors
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Migration from == to errors.Is
|
|
192
|
+
|
|
193
|
+
**Before (broken with wrapping):**
|
|
194
|
+
```go
|
|
195
|
+
// WRONG — breaks as soon as anyone wraps the error
|
|
196
|
+
if err == sql.ErrNoRows {
|
|
197
|
+
return nil, ErrNotFound
|
|
198
|
+
}
|
|
199
|
+
if err == context.DeadlineExceeded {
|
|
200
|
+
return nil, ErrTimeout
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**After (works through any chain):**
|
|
205
|
+
```go
|
|
206
|
+
// CORRECT — works with any level of wrapping
|
|
207
|
+
if errors.Is(err, sql.ErrNoRows) {
|
|
208
|
+
return nil, ErrNotFound
|
|
209
|
+
}
|
|
210
|
+
if errors.Is(err, context.DeadlineExceeded) {
|
|
211
|
+
return nil, ErrTimeout
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Migration checklist:**
|
|
216
|
+
|
|
217
|
+
| Old Pattern | New Pattern |
|
|
218
|
+
|-------------|-------------|
|
|
219
|
+
| `err == ErrX` | `errors.Is(err, ErrX)` |
|
|
220
|
+
| `err != ErrX` | `!errors.Is(err, ErrX)` |
|
|
221
|
+
| `err.(*TypeX)` | `errors.As(err, &target)` |
|
|
222
|
+
| `errors.As(err, &target)` | `errors.AsType[T](err)` (Go 1.26+) |
|
|
223
|
+
| `switch err.(type)` | Sequential `errors.As` checks |
|
|
224
|
+
| `err == nil` | Keep as `err == nil` (no change needed) |
|
|
225
|
+
|
|
226
|
+
## Common Mistakes
|
|
227
|
+
|
|
228
|
+
### 1. Comparing Error Strings
|
|
229
|
+
|
|
230
|
+
```go
|
|
231
|
+
// WRONG — fragile, locale-dependent, breaks on wrapping
|
|
232
|
+
if err.Error() == "connection refused" {
|
|
233
|
+
// retry
|
|
234
|
+
}
|
|
235
|
+
if strings.Contains(err.Error(), "timeout") {
|
|
236
|
+
// retry
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// CORRECT — use sentinel or type check
|
|
240
|
+
if errors.Is(err, syscall.ECONNREFUSED) {
|
|
241
|
+
// retry
|
|
242
|
+
}
|
|
243
|
+
if errors.Is(err, context.DeadlineExceeded) {
|
|
244
|
+
// retry
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 2. Type-Asserting Wrapped Errors
|
|
249
|
+
|
|
250
|
+
```go
|
|
251
|
+
// WRONG — fails when error is wrapped
|
|
252
|
+
if verr, ok := err.(*ValidationError); ok {
|
|
253
|
+
// This won't match: fmt.Errorf("svc: %w", &ValidationError{...})
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// CORRECT — traverses the chain
|
|
257
|
+
var verr *ValidationError
|
|
258
|
+
if errors.As(err, &verr) {
|
|
259
|
+
// Always finds it, regardless of wrapping depth
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### 3. Checking for nil After errors.As
|
|
264
|
+
|
|
265
|
+
```go
|
|
266
|
+
// WRONG — errors.As already confirms non-nil
|
|
267
|
+
var verr *ValidationError
|
|
268
|
+
if errors.As(err, &verr) {
|
|
269
|
+
if verr != nil { // redundant — always true here
|
|
270
|
+
// ...
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// CORRECT — verr is guaranteed non-nil inside the if
|
|
275
|
+
var verr *ValidationError
|
|
276
|
+
if errors.As(err, &verr) {
|
|
277
|
+
log.Warn("validation", "field", verr.Field)
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### 4. Wrong errors.As Target Type
|
|
282
|
+
|
|
283
|
+
```go
|
|
284
|
+
// WRONG — target must be pointer to the error type (or pointer-to-pointer)
|
|
285
|
+
var verr ValidationError // not a pointer!
|
|
286
|
+
if errors.As(err, &verr) { // compile error or unexpected behavior
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// CORRECT — pointer to pointer for pointer-receiver error types
|
|
290
|
+
var verr *ValidationError
|
|
291
|
+
if errors.As(err, &verr) {
|
|
292
|
+
// verr is *ValidationError
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### 5. Ignoring Multi-Error Branches
|
|
297
|
+
|
|
298
|
+
```go
|
|
299
|
+
// WRONG — only checks first error
|
|
300
|
+
err := errors.Join(errA, errB, errC)
|
|
301
|
+
if errors.Unwrap(err) == errA { // only gets first
|
|
302
|
+
|
|
303
|
+
// CORRECT — errors.Is checks all branches
|
|
304
|
+
if errors.Is(err, errA) { // checks errA, errB, errC
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Comprehensive Error Handling Pattern
|
|
308
|
+
|
|
309
|
+
```go
|
|
310
|
+
func (h *Handler) HandleRequest(w http.ResponseWriter, r *http.Request) {
|
|
311
|
+
result, err := h.svc.Process(r.Context(), input)
|
|
312
|
+
if err != nil {
|
|
313
|
+
// Check domain errors first (most specific)
|
|
314
|
+
var verr *ValidationError
|
|
315
|
+
if errors.As(err, &verr) {
|
|
316
|
+
respondJSON(w, http.StatusBadRequest, ErrorResponse{
|
|
317
|
+
Code: "VALIDATION_ERROR",
|
|
318
|
+
Message: verr.Error(),
|
|
319
|
+
Field: verr.Field,
|
|
320
|
+
})
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Check sentinels
|
|
325
|
+
switch {
|
|
326
|
+
case errors.Is(err, domain.ErrNotFound):
|
|
327
|
+
respondJSON(w, http.StatusNotFound, ErrorResponse{Code: "NOT_FOUND"})
|
|
328
|
+
case errors.Is(err, domain.ErrForbidden):
|
|
329
|
+
respondJSON(w, http.StatusForbidden, ErrorResponse{Code: "FORBIDDEN"})
|
|
330
|
+
case errors.Is(err, context.DeadlineExceeded):
|
|
331
|
+
respondJSON(w, http.StatusGatewayTimeout, ErrorResponse{Code: "TIMEOUT"})
|
|
332
|
+
default:
|
|
333
|
+
h.logger.Error("unhandled error", "error", err)
|
|
334
|
+
respondJSON(w, http.StatusInternalServerError, ErrorResponse{Code: "INTERNAL"})
|
|
335
|
+
}
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
respondJSON(w, http.StatusOK, result)
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## When to Use
|
|
344
|
+
|
|
345
|
+
Apply when reviewing error checking and inspection patterns. Flag violations as:
|
|
346
|
+
- **CRITICAL**: Using `==` instead of `errors.Is` for sentinel comparison, type assertion instead of `errors.As` for wrapped errors
|
|
347
|
+
- **WARNING**: String comparison for error checking, ignoring multi-error semantics
|
|
348
|
+
- **SUGGESTION**: Could use custom Is/As methods, redundant nil checks after errors.As
|