cfsa-antigravity 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/package.json +1 -1
- package/template/.agent/instructions/commands.md +8 -32
- package/template/.agent/instructions/example.md +21 -0
- package/template/.agent/instructions/patterns.md +3 -3
- package/template/.agent/instructions/tech-stack.md +71 -23
- package/template/.agent/instructions/workflow.md +12 -1
- package/template/.agent/rules/completion-checklist.md +6 -0
- package/template/.agent/rules/security-first.md +3 -3
- package/template/.agent/rules/vertical-slices.md +1 -1
- package/template/.agent/skill-library/MANIFEST.md +6 -0
- package/template/.agent/skill-library/stack/devops/git-advanced/SKILL.md +972 -0
- package/template/.agent/skill-library/stack/devops/git-workflow/SKILL.md +420 -0
- package/template/.agent/skills/api-versioning/SKILL.md +44 -298
- package/template/.agent/skills/api-versioning/references/typescript.md +157 -0
- package/template/.agent/skills/architecture-mapping/SKILL.md +13 -13
- package/template/.agent/skills/bootstrap-agents/SKILL.md +151 -152
- package/template/.agent/skills/clean-code/SKILL.md +64 -118
- package/template/.agent/skills/clean-code/references/typescript.md +126 -0
- package/template/.agent/skills/database-schema-design/SKILL.md +93 -317
- package/template/.agent/skills/database-schema-design/references/relational.md +228 -0
- package/template/.agent/skills/error-handling-patterns/SKILL.md +62 -557
- package/template/.agent/skills/error-handling-patterns/references/go.md +162 -0
- package/template/.agent/skills/error-handling-patterns/references/python.md +262 -0
- package/template/.agent/skills/error-handling-patterns/references/rust.md +112 -0
- package/template/.agent/skills/error-handling-patterns/references/typescript.md +178 -0
- package/template/.agent/skills/idea-extraction/SKILL.md +322 -224
- package/template/.agent/skills/logging-best-practices/SKILL.md +108 -767
- package/template/.agent/skills/logging-best-practices/references/go.md +49 -0
- package/template/.agent/skills/logging-best-practices/references/python.md +52 -0
- package/template/.agent/skills/logging-best-practices/references/typescript.md +215 -0
- package/template/.agent/skills/migration-management/SKILL.md +127 -311
- package/template/.agent/skills/migration-management/references/relational.md +214 -0
- package/template/.agent/skills/parallel-feature-development/SKILL.md +34 -43
- package/template/.agent/skills/pipeline-rubrics/references/be-rubric.md +1 -1
- package/template/.agent/skills/pipeline-rubrics/references/ia-rubric.md +2 -2
- package/template/.agent/skills/pipeline-rubrics/references/scoring.md +1 -1
- package/template/.agent/skills/pipeline-rubrics/references/vision-rubric.md +2 -1
- package/template/.agent/skills/prd-templates/SKILL.md +23 -6
- package/template/.agent/skills/prd-templates/references/be-spec-template.md +2 -2
- package/template/.agent/skills/prd-templates/references/decomposition-templates.md +2 -2
- package/template/.agent/skills/prd-templates/references/engineering-standards-template.md +2 -0
- package/template/.agent/skills/prd-templates/references/fe-spec-template.md +1 -1
- package/template/.agent/skills/prd-templates/references/fractal-cx-template.md +58 -0
- package/template/.agent/skills/prd-templates/references/fractal-feature-template.md +93 -0
- package/template/.agent/skills/prd-templates/references/fractal-node-index-template.md +55 -0
- package/template/.agent/skills/prd-templates/references/ideation-crosscut-template.md +26 -47
- package/template/.agent/skills/prd-templates/references/ideation-index-template.md +47 -31
- package/template/.agent/skills/prd-templates/references/operational-templates.md +1 -1
- package/template/.agent/skills/prd-templates/references/placeholder-workflow-mapping.md +50 -21
- package/template/.agent/skills/prd-templates/references/skill-loading-protocol.md +32 -0
- package/template/.agent/skills/prd-templates/references/slice-completion-gates.md +29 -0
- package/template/.agent/skills/prd-templates/references/spec-coverage-sweep.md +3 -3
- package/template/.agent/skills/prd-templates/references/tdd-testing-policy.md +39 -0
- package/template/.agent/skills/prd-templates/references/vision-template.md +8 -8
- package/template/.agent/skills/regex-patterns/SKILL.md +122 -540
- package/template/.agent/skills/regex-patterns/references/go.md +44 -0
- package/template/.agent/skills/regex-patterns/references/javascript.md +63 -0
- package/template/.agent/skills/regex-patterns/references/python.md +77 -0
- package/template/.agent/skills/regex-patterns/references/rust.md +43 -0
- package/template/.agent/skills/resolve-ambiguity/SKILL.md +1 -1
- package/template/.agent/skills/session-continuity/SKILL.md +11 -9
- package/template/.agent/skills/session-continuity/protocols/02-progress-generation.md +2 -2
- package/template/.agent/skills/session-continuity/protocols/04-pattern-extraction.md +1 -1
- package/template/.agent/skills/session-continuity/protocols/05-session-close.md +1 -1
- package/template/.agent/skills/session-continuity/protocols/09-parallel-claim.md +1 -1
- package/template/.agent/skills/session-continuity/protocols/10-placeholder-verification-gate.md +57 -78
- package/template/.agent/skills/session-continuity/protocols/11-parallel-synthesis.md +1 -1
- package/template/.agent/skills/spec-writing/SKILL.md +1 -1
- package/template/.agent/skills/tdd-workflow/SKILL.md +94 -317
- package/template/.agent/skills/tdd-workflow/references/typescript.md +231 -0
- package/template/.agent/skills/testing-strategist/SKILL.md +74 -687
- package/template/.agent/skills/testing-strategist/references/typescript.md +328 -0
- package/template/.agent/skills/workflow-automation/SKILL.md +62 -154
- package/template/.agent/skills/workflow-automation/references/inngest.md +88 -0
- package/template/.agent/skills/workflow-automation/references/temporal.md +64 -0
- package/template/.agent/workflows/bootstrap-agents-fill.md +85 -143
- package/template/.agent/workflows/bootstrap-agents-provision.md +90 -107
- package/template/.agent/workflows/create-prd-architecture.md +23 -16
- package/template/.agent/workflows/create-prd-compile.md +11 -12
- package/template/.agent/workflows/create-prd-design-system.md +1 -1
- package/template/.agent/workflows/create-prd-security.md +9 -11
- package/template/.agent/workflows/create-prd-stack.md +10 -4
- package/template/.agent/workflows/create-prd.md +9 -9
- package/template/.agent/workflows/decompose-architecture-structure.md +4 -6
- package/template/.agent/workflows/decompose-architecture-validate.md +18 -1
- package/template/.agent/workflows/decompose-architecture.md +18 -3
- package/template/.agent/workflows/evolve-contract.md +11 -11
- package/template/.agent/workflows/evolve-feature-classify.md +14 -6
- package/template/.agent/workflows/ideate-discover.md +72 -107
- package/template/.agent/workflows/ideate-extract.md +84 -63
- package/template/.agent/workflows/ideate-validate.md +26 -22
- package/template/.agent/workflows/ideate.md +9 -9
- package/template/.agent/workflows/implement-slice-setup.md +25 -23
- package/template/.agent/workflows/implement-slice-tdd.md +73 -89
- package/template/.agent/workflows/implement-slice.md +4 -4
- package/template/.agent/workflows/plan-phase-preflight.md +6 -2
- package/template/.agent/workflows/plan-phase-write.md +6 -8
- package/template/.agent/workflows/remediate-pipeline-assess.md +2 -1
- package/template/.agent/workflows/resolve-ambiguity.md +2 -2
- package/template/.agent/workflows/update-architecture-map.md +22 -5
- package/template/.agent/workflows/validate-phase-quality.md +155 -0
- package/template/.agent/workflows/validate-phase-readiness.md +167 -0
- package/template/.agent/workflows/validate-phase.md +19 -157
- package/template/.agent/workflows/verify-infrastructure.md +10 -10
- package/template/.agent/workflows/write-architecture-spec-design.md +23 -14
- package/template/.agent/workflows/write-be-spec-classify.md +25 -21
- package/template/.agent/workflows/write-be-spec.md +1 -1
- package/template/.agent/workflows/write-fe-spec-classify.md +6 -12
- package/template/.agent/workflows/write-fe-spec-write.md +1 -1
- package/template/AGENTS.md +6 -2
- package/template/GEMINI.md +5 -3
- package/template/docs/README.md +10 -10
- package/template/docs/kit-architecture.md +126 -33
- package/template/docs/plans/ideation/README.md +8 -3
- package/template/.agent/skills/prd-templates/references/ideation-domain-template.md +0 -55
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Go Error Handling Patterns
|
|
2
|
+
|
|
3
|
+
Language-specific patterns for the `error-handling-patterns` skill. Read `SKILL.md` first for universal methodology.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Explicit Error Returns
|
|
8
|
+
|
|
9
|
+
```go
|
|
10
|
+
// Basic error handling
|
|
11
|
+
func getUser(id string) (*User, error) {
|
|
12
|
+
user, err := db.QueryUser(id)
|
|
13
|
+
if err != nil {
|
|
14
|
+
return nil, fmt.Errorf("failed to query user: %w", err)
|
|
15
|
+
}
|
|
16
|
+
if user == nil {
|
|
17
|
+
return nil, errors.New("user not found")
|
|
18
|
+
}
|
|
19
|
+
return user, nil
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Custom error types
|
|
23
|
+
type ValidationError struct {
|
|
24
|
+
Field string
|
|
25
|
+
Message string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func (e *ValidationError) Error() string {
|
|
29
|
+
return fmt.Sprintf("validation failed for %s: %s", e.Field, e.Message)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Sentinel errors for comparison
|
|
33
|
+
var (
|
|
34
|
+
ErrNotFound = errors.New("not found")
|
|
35
|
+
ErrUnauthorized = errors.New("unauthorized")
|
|
36
|
+
ErrInvalidInput = errors.New("invalid input")
|
|
37
|
+
)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Error Checking and Wrapping
|
|
41
|
+
|
|
42
|
+
```go
|
|
43
|
+
// Error checking with errors.Is
|
|
44
|
+
user, err := getUser("123")
|
|
45
|
+
if err != nil {
|
|
46
|
+
if errors.Is(err, ErrNotFound) {
|
|
47
|
+
// Handle not found
|
|
48
|
+
} else {
|
|
49
|
+
// Handle other errors
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Error wrapping preserves the chain
|
|
54
|
+
func processUser(id string) error {
|
|
55
|
+
user, err := getUser(id)
|
|
56
|
+
if err != nil {
|
|
57
|
+
return fmt.Errorf("process user failed: %w", err)
|
|
58
|
+
}
|
|
59
|
+
// Process user
|
|
60
|
+
return nil
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Unwrap to specific error types
|
|
64
|
+
err := processUser("123")
|
|
65
|
+
if err != nil {
|
|
66
|
+
var valErr *ValidationError
|
|
67
|
+
if errors.As(err, &valErr) {
|
|
68
|
+
fmt.Printf("Validation error: %s\n", valErr.Field)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Defer for Cleanup
|
|
74
|
+
|
|
75
|
+
```go
|
|
76
|
+
func readFile(path string) (string, error) {
|
|
77
|
+
f, err := os.Open(path)
|
|
78
|
+
if err != nil {
|
|
79
|
+
return "", fmt.Errorf("open file: %w", err)
|
|
80
|
+
}
|
|
81
|
+
defer f.Close() // Always runs, even on error
|
|
82
|
+
|
|
83
|
+
data, err := io.ReadAll(f)
|
|
84
|
+
if err != nil {
|
|
85
|
+
return "", fmt.Errorf("read file: %w", err)
|
|
86
|
+
}
|
|
87
|
+
return string(data), nil
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Defer with error handling
|
|
91
|
+
func writeData(path string, data []byte) (err error) {
|
|
92
|
+
f, err := os.Create(path)
|
|
93
|
+
if err != nil {
|
|
94
|
+
return fmt.Errorf("create file: %w", err)
|
|
95
|
+
}
|
|
96
|
+
defer func() {
|
|
97
|
+
closeErr := f.Close()
|
|
98
|
+
if err == nil {
|
|
99
|
+
err = closeErr
|
|
100
|
+
}
|
|
101
|
+
}()
|
|
102
|
+
|
|
103
|
+
_, err = f.Write(data)
|
|
104
|
+
return err
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Panic and Recover
|
|
109
|
+
|
|
110
|
+
```go
|
|
111
|
+
// Panic for unrecoverable errors (programming bugs)
|
|
112
|
+
func mustParseConfig(path string) Config {
|
|
113
|
+
cfg, err := parseConfig(path)
|
|
114
|
+
if err != nil {
|
|
115
|
+
panic(fmt.Sprintf("config parse failed: %v", err))
|
|
116
|
+
}
|
|
117
|
+
return cfg
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Recover in middleware/handlers to prevent crashes
|
|
121
|
+
func recoveryMiddleware(next http.Handler) http.Handler {
|
|
122
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
123
|
+
defer func() {
|
|
124
|
+
if err := recover(); err != nil {
|
|
125
|
+
log.Printf("panic recovered: %v\n%s", err, debug.Stack())
|
|
126
|
+
http.Error(w, "Internal Server Error", 500)
|
|
127
|
+
}
|
|
128
|
+
}()
|
|
129
|
+
next.ServeHTTP(w, r)
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Error Groups for Concurrent Operations
|
|
135
|
+
|
|
136
|
+
```go
|
|
137
|
+
import "golang.org/x/sync/errgroup"
|
|
138
|
+
|
|
139
|
+
func fetchAllData(ctx context.Context) (*AllData, error) {
|
|
140
|
+
g, ctx := errgroup.WithContext(ctx)
|
|
141
|
+
var users []User
|
|
142
|
+
var orders []Order
|
|
143
|
+
|
|
144
|
+
g.Go(func() error {
|
|
145
|
+
var err error
|
|
146
|
+
users, err = fetchUsers(ctx)
|
|
147
|
+
return err
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
g.Go(func() error {
|
|
151
|
+
var err error
|
|
152
|
+
orders, err = fetchOrders(ctx)
|
|
153
|
+
return err
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
if err := g.Wait(); err != nil {
|
|
157
|
+
return nil, fmt.Errorf("fetch all data: %w", err)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return &AllData{Users: users, Orders: orders}, nil
|
|
161
|
+
}
|
|
162
|
+
```
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Python Error Handling Patterns
|
|
2
|
+
|
|
3
|
+
Language-specific patterns for the `error-handling-patterns` skill. Read `SKILL.md` first for universal methodology.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Custom Exception Hierarchy
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
class ApplicationError(Exception):
|
|
11
|
+
"""Base exception for all application errors."""
|
|
12
|
+
def __init__(self, message: str, code: str = None, details: dict = None):
|
|
13
|
+
super().__init__(message)
|
|
14
|
+
self.code = code
|
|
15
|
+
self.details = details or {}
|
|
16
|
+
self.timestamp = datetime.utcnow()
|
|
17
|
+
|
|
18
|
+
class ValidationError(ApplicationError):
|
|
19
|
+
"""Raised when validation fails."""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
class NotFoundError(ApplicationError):
|
|
23
|
+
"""Raised when resource not found."""
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
class ExternalServiceError(ApplicationError):
|
|
27
|
+
"""Raised when external service fails."""
|
|
28
|
+
def __init__(self, message: str, service: str, **kwargs):
|
|
29
|
+
super().__init__(message, **kwargs)
|
|
30
|
+
self.service = service
|
|
31
|
+
|
|
32
|
+
# Usage
|
|
33
|
+
def get_user(user_id: str) -> User:
|
|
34
|
+
user = db.query(User).filter_by(id=user_id).first()
|
|
35
|
+
if not user:
|
|
36
|
+
raise NotFoundError(
|
|
37
|
+
f"User not found",
|
|
38
|
+
code="USER_NOT_FOUND",
|
|
39
|
+
details={"user_id": user_id}
|
|
40
|
+
)
|
|
41
|
+
return user
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Context Managers for Cleanup
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from contextlib import contextmanager
|
|
48
|
+
|
|
49
|
+
@contextmanager
|
|
50
|
+
def database_transaction(session):
|
|
51
|
+
"""Ensure transaction is committed or rolled back."""
|
|
52
|
+
try:
|
|
53
|
+
yield session
|
|
54
|
+
session.commit()
|
|
55
|
+
except Exception as e:
|
|
56
|
+
session.rollback()
|
|
57
|
+
raise
|
|
58
|
+
finally:
|
|
59
|
+
session.close()
|
|
60
|
+
|
|
61
|
+
# Usage
|
|
62
|
+
with database_transaction(db.session) as session:
|
|
63
|
+
user = User(name="Alice")
|
|
64
|
+
session.add(user)
|
|
65
|
+
# Automatic commit or rollback
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Retry with Exponential Backoff
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import time
|
|
72
|
+
from functools import wraps
|
|
73
|
+
from typing import TypeVar, Callable
|
|
74
|
+
|
|
75
|
+
T = TypeVar('T')
|
|
76
|
+
|
|
77
|
+
def retry(
|
|
78
|
+
max_attempts: int = 3,
|
|
79
|
+
backoff_factor: float = 2.0,
|
|
80
|
+
exceptions: tuple = (Exception,)
|
|
81
|
+
):
|
|
82
|
+
"""Retry decorator with exponential backoff."""
|
|
83
|
+
def decorator(func: Callable[..., T]) -> Callable[..., T]:
|
|
84
|
+
@wraps(func)
|
|
85
|
+
def wrapper(*args, **kwargs) -> T:
|
|
86
|
+
last_exception = None
|
|
87
|
+
for attempt in range(max_attempts):
|
|
88
|
+
try:
|
|
89
|
+
return func(*args, **kwargs)
|
|
90
|
+
except exceptions as e:
|
|
91
|
+
last_exception = e
|
|
92
|
+
if attempt < max_attempts - 1:
|
|
93
|
+
sleep_time = backoff_factor ** attempt
|
|
94
|
+
time.sleep(sleep_time)
|
|
95
|
+
continue
|
|
96
|
+
raise
|
|
97
|
+
raise last_exception
|
|
98
|
+
return wrapper
|
|
99
|
+
return decorator
|
|
100
|
+
|
|
101
|
+
# Usage
|
|
102
|
+
@retry(max_attempts=3, exceptions=(NetworkError,))
|
|
103
|
+
def fetch_data(url: str) -> dict:
|
|
104
|
+
response = requests.get(url, timeout=5)
|
|
105
|
+
response.raise_for_status()
|
|
106
|
+
return response.json()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Circuit Breaker
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from enum import Enum
|
|
113
|
+
from datetime import datetime, timedelta
|
|
114
|
+
from typing import Callable, TypeVar
|
|
115
|
+
|
|
116
|
+
T = TypeVar('T')
|
|
117
|
+
|
|
118
|
+
class CircuitState(Enum):
|
|
119
|
+
CLOSED = "closed" # Normal operation
|
|
120
|
+
OPEN = "open" # Failing, reject requests
|
|
121
|
+
HALF_OPEN = "half_open" # Testing if recovered
|
|
122
|
+
|
|
123
|
+
class CircuitBreaker:
|
|
124
|
+
def __init__(
|
|
125
|
+
self,
|
|
126
|
+
failure_threshold: int = 5,
|
|
127
|
+
timeout: timedelta = timedelta(seconds=60),
|
|
128
|
+
success_threshold: int = 2
|
|
129
|
+
):
|
|
130
|
+
self.failure_threshold = failure_threshold
|
|
131
|
+
self.timeout = timeout
|
|
132
|
+
self.success_threshold = success_threshold
|
|
133
|
+
self.failure_count = 0
|
|
134
|
+
self.success_count = 0
|
|
135
|
+
self.state = CircuitState.CLOSED
|
|
136
|
+
self.last_failure_time = None
|
|
137
|
+
|
|
138
|
+
def call(self, func: Callable[[], T]) -> T:
|
|
139
|
+
if self.state == CircuitState.OPEN:
|
|
140
|
+
if datetime.now() - self.last_failure_time > self.timeout:
|
|
141
|
+
self.state = CircuitState.HALF_OPEN
|
|
142
|
+
self.success_count = 0
|
|
143
|
+
else:
|
|
144
|
+
raise Exception("Circuit breaker is OPEN")
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
result = func()
|
|
148
|
+
self.on_success()
|
|
149
|
+
return result
|
|
150
|
+
except Exception as e:
|
|
151
|
+
self.on_failure()
|
|
152
|
+
raise
|
|
153
|
+
|
|
154
|
+
def on_success(self):
|
|
155
|
+
self.failure_count = 0
|
|
156
|
+
if self.state == CircuitState.HALF_OPEN:
|
|
157
|
+
self.success_count += 1
|
|
158
|
+
if self.success_count >= self.success_threshold:
|
|
159
|
+
self.state = CircuitState.CLOSED
|
|
160
|
+
self.success_count = 0
|
|
161
|
+
|
|
162
|
+
def on_failure(self):
|
|
163
|
+
self.failure_count += 1
|
|
164
|
+
self.last_failure_time = datetime.now()
|
|
165
|
+
if self.failure_count >= self.failure_threshold:
|
|
166
|
+
self.state = CircuitState.OPEN
|
|
167
|
+
|
|
168
|
+
# Usage
|
|
169
|
+
circuit_breaker = CircuitBreaker()
|
|
170
|
+
|
|
171
|
+
def fetch_data():
|
|
172
|
+
return circuit_breaker.call(lambda: external_api.get_data())
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Graceful Degradation
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from typing import Optional, Callable, TypeVar
|
|
179
|
+
|
|
180
|
+
T = TypeVar('T')
|
|
181
|
+
|
|
182
|
+
def with_fallback(
|
|
183
|
+
primary: Callable[[], T],
|
|
184
|
+
fallback: Callable[[], T],
|
|
185
|
+
log_error: bool = True
|
|
186
|
+
) -> T:
|
|
187
|
+
"""Try primary function, fall back to fallback on error."""
|
|
188
|
+
try:
|
|
189
|
+
return primary()
|
|
190
|
+
except Exception as e:
|
|
191
|
+
if log_error:
|
|
192
|
+
logger.error(f"Primary function failed: {e}")
|
|
193
|
+
return fallback()
|
|
194
|
+
|
|
195
|
+
# Usage
|
|
196
|
+
def get_user_profile(user_id: str) -> UserProfile:
|
|
197
|
+
return with_fallback(
|
|
198
|
+
primary=lambda: fetch_from_cache(user_id),
|
|
199
|
+
fallback=lambda: fetch_from_database(user_id)
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Multiple fallbacks
|
|
203
|
+
def get_exchange_rate(currency: str) -> float:
|
|
204
|
+
return (
|
|
205
|
+
try_function(lambda: api_provider_1.get_rate(currency))
|
|
206
|
+
or try_function(lambda: api_provider_2.get_rate(currency))
|
|
207
|
+
or try_function(lambda: cache.get_rate(currency))
|
|
208
|
+
or DEFAULT_RATE
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
def try_function(func: Callable[[], Optional[T]]) -> Optional[T]:
|
|
212
|
+
try:
|
|
213
|
+
return func()
|
|
214
|
+
except Exception:
|
|
215
|
+
return None
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Comprehensive Example
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
def process_order(order_id: str) -> Order:
|
|
222
|
+
"""Process order with comprehensive error handling."""
|
|
223
|
+
try:
|
|
224
|
+
# Validate input
|
|
225
|
+
if not order_id:
|
|
226
|
+
raise ValidationError("Order ID is required")
|
|
227
|
+
|
|
228
|
+
# Fetch order
|
|
229
|
+
order = db.get_order(order_id)
|
|
230
|
+
if not order:
|
|
231
|
+
raise NotFoundError("Order", order_id)
|
|
232
|
+
|
|
233
|
+
# Process payment
|
|
234
|
+
try:
|
|
235
|
+
payment_result = payment_service.charge(order.total)
|
|
236
|
+
except PaymentServiceError as e:
|
|
237
|
+
# Log and wrap external service error
|
|
238
|
+
logger.error(f"Payment failed for order {order_id}: {e}")
|
|
239
|
+
raise ExternalServiceError(
|
|
240
|
+
f"Payment processing failed",
|
|
241
|
+
service="payment_service",
|
|
242
|
+
details={"order_id": order_id, "amount": order.total}
|
|
243
|
+
) from e
|
|
244
|
+
|
|
245
|
+
# Update order
|
|
246
|
+
order.status = "completed"
|
|
247
|
+
order.payment_id = payment_result.id
|
|
248
|
+
db.save(order)
|
|
249
|
+
|
|
250
|
+
return order
|
|
251
|
+
|
|
252
|
+
except ApplicationError:
|
|
253
|
+
# Re-raise known application errors
|
|
254
|
+
raise
|
|
255
|
+
except Exception as e:
|
|
256
|
+
# Log unexpected errors
|
|
257
|
+
logger.exception(f"Unexpected error processing order {order_id}")
|
|
258
|
+
raise ApplicationError(
|
|
259
|
+
"Order processing failed",
|
|
260
|
+
code="INTERNAL_ERROR"
|
|
261
|
+
) from e
|
|
262
|
+
```
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Rust Error Handling Patterns
|
|
2
|
+
|
|
3
|
+
Language-specific patterns for the `error-handling-patterns` skill. Read `SKILL.md` first for universal methodology.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Result and Option Types
|
|
8
|
+
|
|
9
|
+
```rust
|
|
10
|
+
use std::fs::File;
|
|
11
|
+
use std::io::{self, Read};
|
|
12
|
+
|
|
13
|
+
// Result type for operations that can fail
|
|
14
|
+
fn read_file(path: &str) -> Result<String, io::Error> {
|
|
15
|
+
let mut file = File::open(path)?; // ? operator propagates errors
|
|
16
|
+
let mut contents = String::new();
|
|
17
|
+
file.read_to_string(&mut contents)?;
|
|
18
|
+
Ok(contents)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Custom error types
|
|
22
|
+
#[derive(Debug)]
|
|
23
|
+
enum AppError {
|
|
24
|
+
Io(io::Error),
|
|
25
|
+
Parse(std::num::ParseIntError),
|
|
26
|
+
NotFound(String),
|
|
27
|
+
Validation(String),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl From<io::Error> for AppError {
|
|
31
|
+
fn from(error: io::Error) -> Self {
|
|
32
|
+
AppError::Io(error)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Using custom error type
|
|
37
|
+
fn read_number_from_file(path: &str) -> Result<i32, AppError> {
|
|
38
|
+
let contents = read_file(path)?; // Auto-converts io::Error
|
|
39
|
+
let number = contents.trim().parse()
|
|
40
|
+
.map_err(AppError::Parse)?; // Explicitly convert ParseIntError
|
|
41
|
+
Ok(number)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Option for nullable values
|
|
45
|
+
fn find_user(id: &str) -> Option<User> {
|
|
46
|
+
users.iter().find(|u| u.id == id).cloned()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Combining Option and Result
|
|
50
|
+
fn get_user_age(id: &str) -> Result<u32, AppError> {
|
|
51
|
+
find_user(id)
|
|
52
|
+
.ok_or_else(|| AppError::NotFound(id.to_string()))
|
|
53
|
+
.map(|user| user.age)
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Error Crate Patterns
|
|
58
|
+
|
|
59
|
+
For production Rust applications, consider using error crates:
|
|
60
|
+
|
|
61
|
+
- **`thiserror`** — derive macro for custom error types with automatic `Display` and `From` implementations
|
|
62
|
+
- **`anyhow`** — flexible error type for applications (not libraries) with context chaining
|
|
63
|
+
- **`eyre`** — enhanced error reporting with `color-eyre` for human-readable error output
|
|
64
|
+
|
|
65
|
+
```rust
|
|
66
|
+
// With thiserror
|
|
67
|
+
use thiserror::Error;
|
|
68
|
+
|
|
69
|
+
#[derive(Error, Debug)]
|
|
70
|
+
enum AppError {
|
|
71
|
+
#[error("IO error: {0}")]
|
|
72
|
+
Io(#[from] io::Error),
|
|
73
|
+
|
|
74
|
+
#[error("Parse error: {0}")]
|
|
75
|
+
Parse(#[from] std::num::ParseIntError),
|
|
76
|
+
|
|
77
|
+
#[error("Resource not found: {0}")]
|
|
78
|
+
NotFound(String),
|
|
79
|
+
|
|
80
|
+
#[error("Validation failed: {0}")]
|
|
81
|
+
Validation(String),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// With anyhow (application code)
|
|
85
|
+
use anyhow::{Context, Result};
|
|
86
|
+
|
|
87
|
+
fn process_config(path: &str) -> Result<Config> {
|
|
88
|
+
let contents = std::fs::read_to_string(path)
|
|
89
|
+
.context("Failed to read config file")?;
|
|
90
|
+
let config: Config = serde_json::from_str(&contents)
|
|
91
|
+
.context("Failed to parse config JSON")?;
|
|
92
|
+
Ok(config)
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Panic vs Result
|
|
97
|
+
|
|
98
|
+
```rust
|
|
99
|
+
// Use Result for expected, recoverable errors
|
|
100
|
+
fn parse_input(s: &str) -> Result<i32, ParseIntError> {
|
|
101
|
+
s.parse()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Use panic for programming bugs / invariant violations
|
|
105
|
+
fn get_element(v: &[i32], index: usize) -> i32 {
|
|
106
|
+
// This is a bug if index is out of bounds — crash is correct
|
|
107
|
+
v[index]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Use .expect() with a message for "should never fail" cases
|
|
111
|
+
let config = load_config().expect("Config file must exist at startup");
|
|
112
|
+
```
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# TypeScript/JavaScript Error Handling Patterns
|
|
2
|
+
|
|
3
|
+
Language-specific patterns for the `error-handling-patterns` skill. Read `SKILL.md` first for universal methodology.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Custom Error Classes
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
class ApplicationError extends Error {
|
|
11
|
+
constructor(
|
|
12
|
+
message: string,
|
|
13
|
+
public code: string,
|
|
14
|
+
public statusCode: number = 500,
|
|
15
|
+
public details?: Record<string, any>,
|
|
16
|
+
) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = this.constructor.name;
|
|
19
|
+
Error.captureStackTrace(this, this.constructor);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class ValidationError extends ApplicationError {
|
|
24
|
+
constructor(message: string, details?: Record<string, any>) {
|
|
25
|
+
super(message, "VALIDATION_ERROR", 400, details);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class NotFoundError extends ApplicationError {
|
|
30
|
+
constructor(resource: string, id: string) {
|
|
31
|
+
super(`${resource} not found`, "NOT_FOUND", 404, { resource, id });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Usage
|
|
36
|
+
function getUser(id: string): User {
|
|
37
|
+
const user = users.find((u) => u.id === id);
|
|
38
|
+
if (!user) {
|
|
39
|
+
throw new NotFoundError("User", id);
|
|
40
|
+
}
|
|
41
|
+
return user;
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Result Type Pattern
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// Result type for explicit error handling
|
|
49
|
+
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
|
|
50
|
+
|
|
51
|
+
// Helper functions
|
|
52
|
+
function Ok<T>(value: T): Result<T, never> {
|
|
53
|
+
return { ok: true, value };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function Err<E>(error: E): Result<never, E> {
|
|
57
|
+
return { ok: false, error };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Usage
|
|
61
|
+
function parseJSON<T>(json: string): Result<T, SyntaxError> {
|
|
62
|
+
try {
|
|
63
|
+
const value = JSON.parse(json) as T;
|
|
64
|
+
return Ok(value);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return Err(error as SyntaxError);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Consuming Result
|
|
71
|
+
const result = parseJSON<User>(userJson);
|
|
72
|
+
if (result.ok) {
|
|
73
|
+
console.log(result.value.name);
|
|
74
|
+
} else {
|
|
75
|
+
console.error("Parse failed:", result.error.message);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Chaining Results
|
|
79
|
+
function chain<T, U, E>(
|
|
80
|
+
result: Result<T, E>,
|
|
81
|
+
fn: (value: T) => Result<U, E>,
|
|
82
|
+
): Result<U, E> {
|
|
83
|
+
return result.ok ? fn(result.value) : result;
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Async Error Handling
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// Async/await with proper error handling
|
|
91
|
+
async function fetchUserOrders(userId: string): Promise<Order[]> {
|
|
92
|
+
try {
|
|
93
|
+
const user = await getUser(userId);
|
|
94
|
+
const orders = await getOrders(user.id);
|
|
95
|
+
return orders;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (error instanceof NotFoundError) {
|
|
98
|
+
return []; // Return empty array for not found
|
|
99
|
+
}
|
|
100
|
+
if (error instanceof NetworkError) {
|
|
101
|
+
// Retry logic
|
|
102
|
+
return retryFetchOrders(userId);
|
|
103
|
+
}
|
|
104
|
+
// Re-throw unexpected errors
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Promise error handling
|
|
110
|
+
function fetchData(url: string): Promise<Data> {
|
|
111
|
+
return fetch(url)
|
|
112
|
+
.then((response) => {
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
throw new NetworkError(`HTTP ${response.status}`);
|
|
115
|
+
}
|
|
116
|
+
return response.json();
|
|
117
|
+
})
|
|
118
|
+
.catch((error) => {
|
|
119
|
+
console.error("Fetch failed:", error);
|
|
120
|
+
throw error;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Error Aggregation
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
class ErrorCollector {
|
|
129
|
+
private errors: Error[] = [];
|
|
130
|
+
|
|
131
|
+
add(error: Error): void {
|
|
132
|
+
this.errors.push(error);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
hasErrors(): boolean {
|
|
136
|
+
return this.errors.length > 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
getErrors(): Error[] {
|
|
140
|
+
return [...this.errors];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
throw(): never {
|
|
144
|
+
if (this.errors.length === 1) {
|
|
145
|
+
throw this.errors[0];
|
|
146
|
+
}
|
|
147
|
+
throw new AggregateError(
|
|
148
|
+
this.errors,
|
|
149
|
+
`${this.errors.length} errors occurred`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Usage: Validate multiple fields
|
|
155
|
+
function validateUser(data: any): User {
|
|
156
|
+
const errors = new ErrorCollector();
|
|
157
|
+
|
|
158
|
+
if (!data.email) {
|
|
159
|
+
errors.add(new ValidationError("Email is required"));
|
|
160
|
+
} else if (!isValidEmail(data.email)) {
|
|
161
|
+
errors.add(new ValidationError("Email is invalid"));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!data.name || data.name.length < 2) {
|
|
165
|
+
errors.add(new ValidationError("Name must be at least 2 characters"));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!data.age || data.age < 18) {
|
|
169
|
+
errors.add(new ValidationError("Age must be 18 or older"));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (errors.hasErrors()) {
|
|
173
|
+
errors.throw();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return data as User;
|
|
177
|
+
}
|
|
178
|
+
```
|