@lvlup-sw/axiom 0.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/.claude-plugin/plugin.json +16 -0
- package/CLAUDE.md +26 -0
- package/LICENSE +21 -0
- package/package.json +37 -0
- package/skills/audit/SKILL.md +126 -0
- package/skills/audit/references/composition-guide.md +105 -0
- package/skills/backend-quality/SKILL.md +40 -0
- package/skills/backend-quality/references/deterministic-checks.md +151 -0
- package/skills/backend-quality/references/dimensions.md +206 -0
- package/skills/backend-quality/references/findings-format.md +61 -0
- package/skills/backend-quality/references/scoring-model.md +86 -0
- package/skills/critique/SKILL.md +132 -0
- package/skills/critique/references/dependency-patterns.md +319 -0
- package/skills/critique/references/solid-principles.md +359 -0
- package/skills/distill/SKILL.md +83 -0
- package/skills/distill/references/dead-code-patterns.md +152 -0
- package/skills/distill/references/simplification-guide.md +128 -0
- package/skills/harden/SKILL.md +161 -0
- package/skills/harden/references/error-patterns.md +180 -0
- package/skills/harden/references/resilience-checklist.md +82 -0
- package/skills/scan/SKILL.md +102 -0
- package/skills/scan/references/check-catalog.md +68 -0
- package/skills/verify/SKILL.md +102 -0
- package/skills/verify/references/contract-testing.md +185 -0
- package/skills/verify/references/test-antipatterns.md +161 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: distill
|
|
3
|
+
description: "Strip backend code to its essence by identifying dead code, vestigial patterns, and unnecessary complexity. Use when cleaning up after refactoring or reducing cognitive load. Triggers: 'simplify code', 'find dead code', 'clean up', or /axiom:distill. Do NOT use for error handling — use axiom:harden instead."
|
|
4
|
+
user-invokable: true
|
|
5
|
+
metadata:
|
|
6
|
+
author: lvlup-sw
|
|
7
|
+
version: 0.1.0
|
|
8
|
+
category: assessment
|
|
9
|
+
dimensions:
|
|
10
|
+
- hygiene
|
|
11
|
+
- topology
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Distill Skill
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
Simplification skill for the axiom backend quality plugin. Covers DIM-5 (Hygiene) and DIM-1 (Topology) dimensions to strip code down to its essential form by identifying dead code, vestigial patterns, and unnecessary complexity.
|
|
19
|
+
|
|
20
|
+
## Triggers
|
|
21
|
+
|
|
22
|
+
Activate this skill when:
|
|
23
|
+
- User says "simplify code", "find dead code", "clean up"
|
|
24
|
+
- User runs `/axiom:distill`
|
|
25
|
+
- Post-refactoring cleanup is needed
|
|
26
|
+
- Reducing cognitive load in a module
|
|
27
|
+
|
|
28
|
+
Do NOT activate when:
|
|
29
|
+
- Error handling improvements are needed — use `axiom:harden` instead
|
|
30
|
+
- Performance optimization is the goal — performance profiling is out of scope for axiom
|
|
31
|
+
- Security hardening is required — use `axiom:harden` instead
|
|
32
|
+
|
|
33
|
+
## Process
|
|
34
|
+
|
|
35
|
+
### 1. Load Dimensions
|
|
36
|
+
|
|
37
|
+
Load the relevant quality dimensions from `@skills/backend-quality/references/dimensions.md`:
|
|
38
|
+
- **DIM-5 (Hygiene):** Dead code, commented-out code, unused imports/exports, vestigial patterns
|
|
39
|
+
- **DIM-1 (Topology):** Module structure, dependency direction, wiring complexity
|
|
40
|
+
|
|
41
|
+
### 2. Run Deterministic Checks
|
|
42
|
+
|
|
43
|
+
Run `axiom:scan` for deterministic checks on Hygiene and Topology dimensions. This produces machine-verifiable findings for:
|
|
44
|
+
- Unused exports and imports
|
|
45
|
+
- Unreachable code paths
|
|
46
|
+
- Circular dependencies
|
|
47
|
+
- Excessive module fan-out
|
|
48
|
+
|
|
49
|
+
### 3. Layer Qualitative Assessment
|
|
50
|
+
|
|
51
|
+
Beyond deterministic checks, apply human-judgment analysis:
|
|
52
|
+
|
|
53
|
+
#### Dead Code Identification
|
|
54
|
+
Identify unreachable branches (code after `return`/`throw`), unused exports (exported but never imported elsewhere), commented-out code (version control exists for history), and feature-flagged-off code that shipped long ago.
|
|
55
|
+
|
|
56
|
+
See `@skills/distill/references/dead-code-patterns.md` for detection heuristics and false positive guidance.
|
|
57
|
+
|
|
58
|
+
#### Vestigial Pattern Detection
|
|
59
|
+
Find evolutionary leftovers from previous designs. Look for divergent implementations that suggest a pattern was partially migrated, adapter layers wrapping things that no longer need adapting, and configuration for features that were removed.
|
|
60
|
+
|
|
61
|
+
#### Wiring Simplification
|
|
62
|
+
Identify manual DI that could be simpler, unnecessary indirection layers, over-abstracted factory/builder patterns where direct construction suffices, and registration ceremonies that add complexity without value.
|
|
63
|
+
|
|
64
|
+
#### Abstraction Audit
|
|
65
|
+
Flag premature abstractions, over-engineering, and single-use helpers. Ask: does this abstraction serve more than one caller? Would inlining it make the code clearer?
|
|
66
|
+
|
|
67
|
+
#### Code Archaeology
|
|
68
|
+
Identify patterns that were once necessary but no longer serve a purpose. Look for workarounds for bugs that have since been fixed, compatibility shims for deprecated APIs, and defensive code guarding against conditions that can no longer occur.
|
|
69
|
+
|
|
70
|
+
### 4. Output Findings
|
|
71
|
+
|
|
72
|
+
Format all findings per `@skills/backend-quality/references/findings-format.md`. Each finding includes:
|
|
73
|
+
- Dimension (DIM-5 or DIM-1)
|
|
74
|
+
- Severity (HIGH, MEDIUM, LOW)
|
|
75
|
+
- Location (file, line range)
|
|
76
|
+
- Description and recommended action
|
|
77
|
+
|
|
78
|
+
See also `@skills/distill/references/simplification-guide.md` for guidance on when to simplify vs remove.
|
|
79
|
+
|
|
80
|
+
## Error Handling
|
|
81
|
+
|
|
82
|
+
- **Empty scope:** If no files match the target scope, output an informative message explaining that no files were found and suggesting the user verify the path or scope parameters.
|
|
83
|
+
- **No findings:** If analysis completes with zero findings, report a clean result rather than failing silently.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Dead Code Patterns
|
|
2
|
+
|
|
3
|
+
Reference guide for identifying, classifying, and triaging dead code in backend systems.
|
|
4
|
+
|
|
5
|
+
## Dead Code Categories
|
|
6
|
+
|
|
7
|
+
### 1. Unreachable Code
|
|
8
|
+
|
|
9
|
+
Code that can never execute because control flow prevents reaching it.
|
|
10
|
+
|
|
11
|
+
**Examples:**
|
|
12
|
+
- Statements after unconditional `return`, `throw`, `break`, or `continue`
|
|
13
|
+
- Branches guarded by conditions that are always false (e.g., `if (false)`, constant expressions)
|
|
14
|
+
- `catch` blocks for exceptions that are never thrown by the `try` body
|
|
15
|
+
- `switch` cases for enum values that no longer exist
|
|
16
|
+
|
|
17
|
+
**Detection heuristics:**
|
|
18
|
+
- Static analysis tools (TypeScript `--noUnusedLocals`, ESLint `no-unreachable`)
|
|
19
|
+
- Grep for code after `return`/`throw` at the same indentation level
|
|
20
|
+
- Analyze control flow graphs for unreachable nodes
|
|
21
|
+
- Look for `if` conditions comparing against removed enum values or deleted constants
|
|
22
|
+
|
|
23
|
+
### 2. Unused Exports
|
|
24
|
+
|
|
25
|
+
Symbols that are exported from a module but never imported by any other module.
|
|
26
|
+
|
|
27
|
+
**Examples:**
|
|
28
|
+
- Functions exported from a utility module that no consumer calls
|
|
29
|
+
- Types/interfaces exported but never referenced externally
|
|
30
|
+
- Re-exports from barrel files (`index.ts`) where the re-exported symbol has no importers
|
|
31
|
+
- Constants exported "just in case" during initial development
|
|
32
|
+
|
|
33
|
+
**Detection heuristics:**
|
|
34
|
+
- Grep for the export name across all importing files; zero hits means unused
|
|
35
|
+
- Use `ts-prune`, `knip`, or similar tools to detect unused exports
|
|
36
|
+
- Analyze barrel file re-exports: if the barrel is imported but only a subset of its exports are used, the rest are dead
|
|
37
|
+
- Check test files separately — an export used only in tests may still be dead from a production perspective
|
|
38
|
+
|
|
39
|
+
### 3. Commented-Out Code
|
|
40
|
+
|
|
41
|
+
Code that has been commented out rather than deleted. Version control exists for history.
|
|
42
|
+
|
|
43
|
+
**Examples:**
|
|
44
|
+
- Block comments containing syntactically valid code
|
|
45
|
+
- Lines prefixed with `//` that contain function calls, variable assignments, or imports
|
|
46
|
+
- `/* ... */` blocks wrapping entire functions or class methods
|
|
47
|
+
- TODO comments referencing code that should be "re-enabled" but never was
|
|
48
|
+
|
|
49
|
+
**Detection heuristics:**
|
|
50
|
+
- Grep for multi-line comments containing keywords like `function`, `const`, `import`, `export`, `class`, `return`
|
|
51
|
+
- Look for `// ` followed by valid statement syntax (assignments, function calls)
|
|
52
|
+
- Identify comments with version control references ("removed in v2", "old implementation")
|
|
53
|
+
- Check git blame — if commented-out code has been that way for more than 2 sprints, it is dead
|
|
54
|
+
|
|
55
|
+
### 4. Feature-Flagged-Off Code
|
|
56
|
+
|
|
57
|
+
Code behind feature flags that have been permanently disabled or the flag has shipped long ago.
|
|
58
|
+
|
|
59
|
+
**Examples:**
|
|
60
|
+
- `if (featureFlags.enableNewParser)` where the flag has been `false` in all environments for months
|
|
61
|
+
- A/B test branches where the experiment concluded and only one path is active
|
|
62
|
+
- Migration code behind a flag that completed its rollout
|
|
63
|
+
- Fallback paths for flags that have been `true` in production for multiple releases
|
|
64
|
+
|
|
65
|
+
**Detection heuristics:**
|
|
66
|
+
- Cross-reference feature flag names with the flag configuration store
|
|
67
|
+
- Analyze flag usage: if a flag is always `true` or always `false` across all environments, the guarded code (or its inverse) is dead
|
|
68
|
+
- Check flag creation dates — flags older than 90 days without recent changes are candidates
|
|
69
|
+
- Look for TODO comments referencing flag cleanup
|
|
70
|
+
|
|
71
|
+
## False Positive Guidance
|
|
72
|
+
|
|
73
|
+
Not all apparently unused code is dead. Be cautious with:
|
|
74
|
+
|
|
75
|
+
- **Intentional stubs:** Interface methods with empty bodies that exist to satisfy a contract, or template methods meant for subclass override
|
|
76
|
+
- **Forward declarations:** Types or constants declared for planned but not-yet-implemented features (check the roadmap/backlog before flagging)
|
|
77
|
+
- **Public API surface:** Libraries and packages intentionally export symbols for external consumers; check if the module is consumed outside the repository
|
|
78
|
+
- **Plugin entry points:** Functions registered via reflection, dependency injection, or naming convention (e.g., `handle_*`, `on_*`) that are invoked dynamically
|
|
79
|
+
- **Test helpers and fixtures:** Utility functions in test support files that may be imported conditionally or by test files not in the current scope
|
|
80
|
+
- **Event handlers and callbacks:** Functions registered as listeners that are invoked indirectly through event dispatch
|
|
81
|
+
|
|
82
|
+
**Verification steps before reporting:**
|
|
83
|
+
1. Search the entire repository, not just the immediate module
|
|
84
|
+
2. Check for dynamic invocation patterns (reflection, `eval`, dynamic imports)
|
|
85
|
+
3. Look for references in configuration files, scripts, and documentation
|
|
86
|
+
4. Verify whether the module is published as a package with external consumers
|
|
87
|
+
|
|
88
|
+
## Severity Guide
|
|
89
|
+
|
|
90
|
+
### HIGH Severity — Misleading Dead Code
|
|
91
|
+
|
|
92
|
+
Dead code that actively misleads developers or creates risk:
|
|
93
|
+
|
|
94
|
+
- Commented-out code that appears to be an alternative implementation, confusing future readers about which path is correct
|
|
95
|
+
- Unreachable error handling that gives false confidence about fault tolerance
|
|
96
|
+
- Unused exports that appear in IDE autocomplete, leading developers to use dead APIs
|
|
97
|
+
- Feature-flagged code where the flag check masks a bug in the "live" path
|
|
98
|
+
|
|
99
|
+
### MEDIUM Severity — Noise
|
|
100
|
+
|
|
101
|
+
Dead code that adds cognitive load without active harm:
|
|
102
|
+
|
|
103
|
+
- Unused utility functions that clutter the module
|
|
104
|
+
- Old commented-out code that has been superseded by a different approach
|
|
105
|
+
- Barrel file re-exports of symbols no one imports
|
|
106
|
+
- Test helpers that are no longer called by any test
|
|
107
|
+
|
|
108
|
+
### LOW Severity — Minor
|
|
109
|
+
|
|
110
|
+
Dead code with minimal impact:
|
|
111
|
+
|
|
112
|
+
- Single unused constants or type aliases
|
|
113
|
+
- Commented-out log/debug statements
|
|
114
|
+
- Unused function parameters (often enforced by interface contracts)
|
|
115
|
+
- Empty catch blocks that were once meaningful but the try body changed
|
|
116
|
+
|
|
117
|
+
## Examples
|
|
118
|
+
|
|
119
|
+
### HIGH: Misleading unreachable error handler
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
async function processOrder(order: Order): Promise<Result> {
|
|
123
|
+
const validated = validateOrder(order);
|
|
124
|
+
return submitOrder(validated);
|
|
125
|
+
|
|
126
|
+
// This catch-all never executes — gives false sense of error handling
|
|
127
|
+
try {
|
|
128
|
+
return submitOrder(validated);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
await notifyOps(error);
|
|
131
|
+
return { status: 'failed', error };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### MEDIUM: Unused export cluttering module
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// utils.ts
|
|
140
|
+
export function formatDate(d: Date): string { /* ... */ }
|
|
141
|
+
export function parseDate(s: string): Date { /* ... */ }
|
|
142
|
+
export function dateToEpoch(d: Date): number { /* ... */ } // zero importers
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### LOW: Commented-out debug logging
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
function handleRequest(req: Request) {
|
|
149
|
+
// console.log('incoming request:', req.headers);
|
|
150
|
+
return processRequest(req);
|
|
151
|
+
}
|
|
152
|
+
```
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Simplification Guide
|
|
2
|
+
|
|
3
|
+
Reference guide for reducing code complexity, identifying vestigial patterns, and deciding when to simplify versus when to remove entirely.
|
|
4
|
+
|
|
5
|
+
## Complexity Reduction Patterns
|
|
6
|
+
|
|
7
|
+
### When to Inline vs Extract
|
|
8
|
+
|
|
9
|
+
**Inline when:**
|
|
10
|
+
- A function is called exactly once and its name does not add clarity beyond reading the body
|
|
11
|
+
- A wrapper function merely delegates to another function without transformation
|
|
12
|
+
- An abstraction layer adds indirection but no behavioral difference (pass-through adapters)
|
|
13
|
+
- The extraction was premature — the "reusable" function is only used in one place
|
|
14
|
+
|
|
15
|
+
**Extract when:**
|
|
16
|
+
- The same logic appears in three or more locations (see "three uses" rule below)
|
|
17
|
+
- A block of code has a clear name that communicates intent better than the implementation details
|
|
18
|
+
- Testing the logic in isolation would significantly improve test coverage or clarity
|
|
19
|
+
- The extracted function represents a domain concept that should be named
|
|
20
|
+
|
|
21
|
+
**Decision heuristic:** If removing the function name forces the reader to re-derive the intent from the implementation, keep the extraction. If the name is just a restatement of the single line it wraps, inline it.
|
|
22
|
+
|
|
23
|
+
### When Abstraction Helps vs Hurts
|
|
24
|
+
|
|
25
|
+
**Abstraction helps when:**
|
|
26
|
+
- Multiple concrete implementations exist and the abstraction captures their shared contract
|
|
27
|
+
- The abstraction enables meaningful testability (swapping real for fake implementations)
|
|
28
|
+
- Domain boundaries are clarified by the interface
|
|
29
|
+
|
|
30
|
+
**Abstraction hurts when:**
|
|
31
|
+
- Only one implementation exists and no second is planned or plausible
|
|
32
|
+
- The abstraction mirrors the implementation 1:1 (an interface with the same shape as the single class)
|
|
33
|
+
- Navigating through the abstraction layer requires more cognitive effort than understanding the concrete code
|
|
34
|
+
- "Dependency injection" is really just passing a single concrete instance through extra layers
|
|
35
|
+
|
|
36
|
+
### Reducing Conditional Complexity
|
|
37
|
+
|
|
38
|
+
- **Collapse nested conditionals:** Replace `if (a) { if (b) { ... } }` with `if (a && b) { ... }` when the nesting adds no clarity
|
|
39
|
+
- **Use early returns:** Convert deep nesting into guard clauses that return/throw early
|
|
40
|
+
- **Replace flag variables:** When a boolean flag is set and then checked once, inline the condition
|
|
41
|
+
- **Simplify boolean expressions:** `if (x === true)` becomes `if (x)`; `if (!x === false)` becomes `if (x)`
|
|
42
|
+
|
|
43
|
+
## Vestigial Pattern Identification
|
|
44
|
+
|
|
45
|
+
### Code Archaeology Approach
|
|
46
|
+
|
|
47
|
+
Vestigial patterns are evolutionary leftovers — code structures that made sense in a previous design but persist after the design changed. To identify them:
|
|
48
|
+
|
|
49
|
+
1. **Look for patterns that reference removed features:** Search for imports of deleted modules, references to renamed types, or configuration keys for features that no longer exist
|
|
50
|
+
2. **Identify partial migrations:** When a codebase migrated from pattern A to pattern B, look for leftover pattern A code that was never converted
|
|
51
|
+
3. **Check adapter layers:** If an adapter wraps a dependency that was replaced, and the adapter's interface matches the new dependency's interface directly, the adapter is vestigial
|
|
52
|
+
4. **Examine defensive code:** Guards against conditions that were possible in a previous version but are now structurally impossible (e.g., null checks after a field became required)
|
|
53
|
+
|
|
54
|
+
### Common Vestigial Pattern Types
|
|
55
|
+
|
|
56
|
+
- **Dead adapters:** Wrapper classes that were introduced to bridge between two APIs, but one API was since removed or the wrapper now delegates directly
|
|
57
|
+
- **Orphaned configuration:** Config keys, environment variables, or feature flags that no active code reads
|
|
58
|
+
- **Compatibility shims:** Polyfills or compatibility layers for platform versions no longer supported
|
|
59
|
+
- **Migration scaffolding:** Temporary code introduced to migrate data or APIs that was never removed after migration completed
|
|
60
|
+
- **Defensive checks for impossible states:** Null checks, type guards, or fallback values for conditions that the current type system or architecture prevents
|
|
61
|
+
|
|
62
|
+
## Wiring Simplification
|
|
63
|
+
|
|
64
|
+
### From Manual Configure/Register to Simpler Patterns
|
|
65
|
+
|
|
66
|
+
**Identify over-engineered wiring when:**
|
|
67
|
+
- A registration function manually lists every dependency and wires them together, but the dependency graph is simple and linear
|
|
68
|
+
- A factory creates objects by resolving dependencies one-by-one when direct construction with `new` would suffice
|
|
69
|
+
- A configuration object has dozens of keys, most of which are always set to the same default value
|
|
70
|
+
- A "plugin system" exists but there is only one plugin and no mechanism for external plugins
|
|
71
|
+
|
|
72
|
+
**Simplification strategies:**
|
|
73
|
+
- Replace manual DI containers with direct construction when there are fewer than 3 dependencies
|
|
74
|
+
- Replace factory functions with constructors when the factory adds no logic beyond `new`
|
|
75
|
+
- Replace configuration objects with sensible defaults and optional overrides
|
|
76
|
+
- Replace event bus / pub-sub patterns with direct function calls when there is only one subscriber
|
|
77
|
+
|
|
78
|
+
### Recognizing Unnecessary Indirection
|
|
79
|
+
|
|
80
|
+
Indirection that does not serve a purpose:
|
|
81
|
+
|
|
82
|
+
- **Pass-through functions:** `function doThing(x) { return actuallyDoThing(x); }` with no additional logic
|
|
83
|
+
- **Single-method interfaces with one implementation:** The interface and class are isomorphic
|
|
84
|
+
- **Manager/controller classes that only delegate:** A `FooManager` that holds a `Foo` and forwards all calls
|
|
85
|
+
- **Middleware chains with one middleware:** The chain infrastructure adds complexity but only one handler exists
|
|
86
|
+
|
|
87
|
+
## The "Three Uses" Rule
|
|
88
|
+
|
|
89
|
+
Do not abstract until you have seen the pattern three times:
|
|
90
|
+
|
|
91
|
+
1. **First occurrence:** Write the code inline. Do not extract.
|
|
92
|
+
2. **Second occurrence:** Note the duplication but tolerate it. Copy-paste is acceptable.
|
|
93
|
+
3. **Third occurrence:** Now extract. You have enough examples to see the true shape of the abstraction.
|
|
94
|
+
|
|
95
|
+
**Why three?** Two occurrences often look similar by coincidence. The third occurrence confirms the pattern is real and reveals which parts vary (parameters) and which are fixed (the abstraction body). Abstracting after two uses risks creating an abstraction shaped for the wrong generalization.
|
|
96
|
+
|
|
97
|
+
**Exception:** If the duplicated code is long (>20 lines) or contains complex logic with known bug risk, extract after two uses. The cost of a slightly wrong abstraction is lower than the cost of a bug fix applied to only one copy.
|
|
98
|
+
|
|
99
|
+
## Simplification vs Deletion
|
|
100
|
+
|
|
101
|
+
### When to Simplify
|
|
102
|
+
|
|
103
|
+
Simplify (reduce complexity without removing) when:
|
|
104
|
+
- The code serves a current purpose but is more complicated than necessary
|
|
105
|
+
- The functionality is needed but the implementation has accumulated accidental complexity
|
|
106
|
+
- A refactoring pass changed the surroundings but left this code with now-unnecessary guards or abstractions
|
|
107
|
+
- The code works but is hard to understand — simplification improves readability without changing behavior
|
|
108
|
+
|
|
109
|
+
### When to Remove Entirely
|
|
110
|
+
|
|
111
|
+
Remove (delete the code) when:
|
|
112
|
+
- The code is unreachable or provably never executed
|
|
113
|
+
- The feature the code supports has been decommissioned
|
|
114
|
+
- The code is commented out and has been for more than one release cycle
|
|
115
|
+
- A replacement implementation exists and the old one is no longer used
|
|
116
|
+
- The code is a workaround for a bug that has been fixed at its source
|
|
117
|
+
|
|
118
|
+
### Decision Framework
|
|
119
|
+
|
|
120
|
+
Ask these questions in order:
|
|
121
|
+
|
|
122
|
+
1. **Is this code reachable?** If no, delete it.
|
|
123
|
+
2. **Does this code serve an active feature?** If no, delete it.
|
|
124
|
+
3. **Is this code more complex than it needs to be?** If yes, simplify it.
|
|
125
|
+
4. **Is there a simpler way to achieve the same result?** If yes, simplify to that.
|
|
126
|
+
5. **Would a reader understand this code without extra context?** If no, simplify for clarity.
|
|
127
|
+
|
|
128
|
+
When in doubt, prefer deletion over simplification. Dead code that is simplified is still dead code. Version control preserves history — you can always retrieve deleted code if needed.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: harden
|
|
3
|
+
description: "Strengthen backend resilience by finding silent catches, missing error context, resource leaks, and operational fragility. Use when hardening error handling or preparing for production deployment. Triggers: 'harden code', 'check error handling', 'resilience review', or /axiom:harden. Do NOT use for dead code — use axiom:distill instead."
|
|
4
|
+
user-invokable: true
|
|
5
|
+
metadata:
|
|
6
|
+
author: lvlup-sw
|
|
7
|
+
version: 0.1.0
|
|
8
|
+
category: assessment
|
|
9
|
+
dimensions:
|
|
10
|
+
- observability
|
|
11
|
+
- resilience
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Harden Skill
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
Resilience-focused assessment skill covering DIM-2 (Observability) and DIM-7 (Resilience) from the backend quality dimension taxonomy. Finds error handling gaps, silent catches, resource leaks, and operational fragility that could cause cascading failures under stress.
|
|
19
|
+
|
|
20
|
+
This skill combines deterministic pattern detection (via `axiom:scan`) with qualitative judgment to assess how well code handles failure, communicates errors, and manages resources.
|
|
21
|
+
|
|
22
|
+
## Triggers
|
|
23
|
+
|
|
24
|
+
### Positive Triggers
|
|
25
|
+
|
|
26
|
+
Activate this skill when:
|
|
27
|
+
- User says "harden code", "harden this", or "resilience review"
|
|
28
|
+
- User says "check error handling" or "audit error handling"
|
|
29
|
+
- User runs `/axiom:harden`
|
|
30
|
+
- Preparing code for production deployment
|
|
31
|
+
- After an incident exposed error handling gaps
|
|
32
|
+
|
|
33
|
+
### Negative Triggers
|
|
34
|
+
|
|
35
|
+
Do NOT use this skill when:
|
|
36
|
+
- Looking for dead code or vestigial patterns — use `axiom:distill` instead
|
|
37
|
+
- Reviewing architecture or SOLID compliance — use `axiom:critique` instead
|
|
38
|
+
- Checking test quality or mock fidelity — use `axiom:verify` instead
|
|
39
|
+
- Running a comprehensive audit — use `axiom:audit` instead
|
|
40
|
+
|
|
41
|
+
## Process
|
|
42
|
+
|
|
43
|
+
### Step 1: Load Dimension Definitions
|
|
44
|
+
|
|
45
|
+
Load the relevant dimensions from the shared taxonomy:
|
|
46
|
+
|
|
47
|
+
- `@skills/backend-quality/references/dimensions.md` — read the DIM-2 (Observability) and DIM-7 (Resilience) sections for invariants, signals, and severity guides.
|
|
48
|
+
|
|
49
|
+
### Step 2: Run Deterministic Checks
|
|
50
|
+
|
|
51
|
+
Invoke `axiom:scan` for mechanical pattern detection on the Observability and Resilience dimensions:
|
|
52
|
+
|
|
53
|
+
- DIM-2 checks: empty catch blocks, log-only catches, swallowed promise rejections
|
|
54
|
+
- DIM-7 checks: unbounded collections, missing timeouts, unbounded retry loops
|
|
55
|
+
|
|
56
|
+
Collect all deterministic findings. These form the baseline that qualitative assessment builds upon.
|
|
57
|
+
|
|
58
|
+
### Step 3: Qualitative Assessment
|
|
59
|
+
|
|
60
|
+
Layer judgment-based analysis on top of the deterministic results. For each area, review the flagged code regions and nearby context:
|
|
61
|
+
|
|
62
|
+
#### 3a. Empty Catch Audit
|
|
63
|
+
|
|
64
|
+
Classify every catch block in scope into one of four categories:
|
|
65
|
+
|
|
66
|
+
| Category | Definition | Action |
|
|
67
|
+
|----------|-----------|--------|
|
|
68
|
+
| **Silent** | Empty catch body, no logging, no recovery | HIGH — must add handling or documented rationale |
|
|
69
|
+
| **Log-only** | Logs the error but takes no recovery action | MEDIUM — evaluate whether recovery is needed |
|
|
70
|
+
| **Recovery** | Catches, logs, and takes corrective action | OK — verify recovery is correct |
|
|
71
|
+
| **Intentional** | Documented rationale for swallowing (e.g., `// Intentional: probe-only, failure is expected`) | OK — verify comment is accurate |
|
|
72
|
+
|
|
73
|
+
Consult `@skills/harden/references/error-patterns.md` for the full silent catch taxonomy and classification guidance.
|
|
74
|
+
|
|
75
|
+
#### 3b. Error Context Propagation
|
|
76
|
+
|
|
77
|
+
For each error path, evaluate whether errors include sufficient context:
|
|
78
|
+
|
|
79
|
+
- **What** failed? (operation name, inputs, resource identifier)
|
|
80
|
+
- **Why** did it fail? (root cause, constraint violation)
|
|
81
|
+
- **How to fix?** (retry guidance, configuration check, escalation path)
|
|
82
|
+
- **Cause chain?** (is the original error preserved via `{ cause: e }`?)
|
|
83
|
+
|
|
84
|
+
Generic error messages like "Something went wrong" or "Operation failed" are a MEDIUM finding.
|
|
85
|
+
|
|
86
|
+
#### 3c. Fallback Behavior Analysis
|
|
87
|
+
|
|
88
|
+
Identify all fallback paths and evaluate visibility:
|
|
89
|
+
|
|
90
|
+
- Are fallbacks logged or metriced so operators know degraded mode is active?
|
|
91
|
+
- Do fallbacks silently switch behavior modes without signaling?
|
|
92
|
+
- Is best-effort behavior clearly documented and visible in monitoring?
|
|
93
|
+
|
|
94
|
+
Silent degradation — where the system quietly switches to a less capable mode — is a HIGH finding.
|
|
95
|
+
|
|
96
|
+
#### 3d. Resource Lifecycle
|
|
97
|
+
|
|
98
|
+
Verify open/close symmetry and acquire/release patterns:
|
|
99
|
+
|
|
100
|
+
- File handles opened in try blocks — are they closed in finally?
|
|
101
|
+
- Database connections — are they released on both success and error paths?
|
|
102
|
+
- Event listeners — are they removed when no longer needed?
|
|
103
|
+
- Streams — are they properly ended/destroyed on error?
|
|
104
|
+
|
|
105
|
+
Consult `@skills/harden/references/resilience-checklist.md` for the full resource management checklist.
|
|
106
|
+
|
|
107
|
+
#### 3e. Timeout and Retry Evaluation
|
|
108
|
+
|
|
109
|
+
For every external call (HTTP, database, file system, IPC):
|
|
110
|
+
|
|
111
|
+
- Is there a timeout? (missing timeout = MEDIUM)
|
|
112
|
+
- Is the timeout reasonable for the operation? (60s for a health check = LOW)
|
|
113
|
+
- Are retries bounded? (unbounded retry = HIGH)
|
|
114
|
+
- Is there backoff? (no backoff = MEDIUM)
|
|
115
|
+
|
|
116
|
+
#### 3f. Cache Bound Verification
|
|
117
|
+
|
|
118
|
+
For every in-memory collection that persists beyond a single request:
|
|
119
|
+
|
|
120
|
+
- Is there a maximum size? (unbounded Map/Set/Array = HIGH)
|
|
121
|
+
- Is there an eviction policy? (LRU, TTL, or manual clear)
|
|
122
|
+
- Do collections grow monotonically without cleanup?
|
|
123
|
+
|
|
124
|
+
### Step 4: Output Findings
|
|
125
|
+
|
|
126
|
+
Format all findings per the standard finding schema: `@skills/backend-quality/references/findings-format.md`
|
|
127
|
+
|
|
128
|
+
Group findings by severity (HIGH, MEDIUM, LOW). Each finding must include:
|
|
129
|
+
- Dimension (DIM-2 or DIM-7)
|
|
130
|
+
- Evidence (file:line references)
|
|
131
|
+
- Explanation (what is wrong and why it matters)
|
|
132
|
+
- Suggestion (how to fix, when actionable)
|
|
133
|
+
- Whether it was found deterministically or qualitatively
|
|
134
|
+
|
|
135
|
+
## Error Handling
|
|
136
|
+
|
|
137
|
+
### Empty Scope
|
|
138
|
+
|
|
139
|
+
If the provided scope is empty or contains no files to analyze, return an informative message:
|
|
140
|
+
|
|
141
|
+
> "No files found in the provided scope. Specify a file path, directory, or glob pattern. Example: `/axiom:harden src/` or `/axiom:harden src/events/`."
|
|
142
|
+
|
|
143
|
+
Do not return an empty finding set without explanation.
|
|
144
|
+
|
|
145
|
+
## Anti-Patterns
|
|
146
|
+
|
|
147
|
+
| Don't | Do Instead |
|
|
148
|
+
|-------|------------|
|
|
149
|
+
| Flag every catch block as a problem | Classify — many catches are correct and intentional |
|
|
150
|
+
| Ignore log-only catches | Evaluate whether the error needs recovery, not just logging |
|
|
151
|
+
| Treat all fallbacks as bad | Evaluate whether degradation is visible and documented |
|
|
152
|
+
| Skip resource lifecycle in test code | Test helpers leak resources too |
|
|
153
|
+
| Assume timeouts are always present | Verify each external call individually |
|
|
154
|
+
| Report cache concerns for request-scoped collections | Only flag persistent/growing collections |
|
|
155
|
+
|
|
156
|
+
## References
|
|
157
|
+
|
|
158
|
+
- `@skills/backend-quality/references/dimensions.md` — DIM-2 and DIM-7 definitions
|
|
159
|
+
- `@skills/backend-quality/references/findings-format.md` — finding output schema
|
|
160
|
+
- `@skills/harden/references/error-patterns.md` — silent catch taxonomy and error context checklist
|
|
161
|
+
- `@skills/harden/references/resilience-checklist.md` — resource management, timeouts, retries, concurrency checklist
|