chore: bootstrap skills library — 19 skills + installer + CI auto-tag
Some checks failed
release / tag (push) Has been cancelled

Phase 1 of mathias/skills extraction (infra#62 Track D — homelab
next-step plan addendum). Imports ~/dev/.skills/ verbatim (19 skill
dirs + SKILLS_INDEX.md) and adds the installation surface:

- Taskfile.yml — install / update / list / release / check targets
- install.sh — bootstrap installer for hosts without Task. Idempotent
  symlink wirer; default checkout at ~/.local/share/skills/ on every
  host; SKILLS_REF env var pins a tag (default: main).
- .gitea/workflows/release.yml — auto-tag every push to main by
  Bump-Type footer (major/minor/patch, default patch). Skipped when
  commit contains [skip-release].
- README — usage, versioning, contribution flow, secret-hygiene rule.

Phase 1 wires Claude Code only (~/.claude/skills/<name> global +
<repo>/.claude/skills/<name> per-repo). Phase 2 adds Crush, opencode,
antigravity, and gitea-resident agents (cobalt-dingo, agentsquad)
once their skill conventions are researched.

Public repo, markdown-only — no secrets, no client names. Verified
via pre-push grep before initial push.

[skip-release]
This commit is contained in:
Mathias
2026-05-24 14:59:54 +02:00
commit d6a71e370e
33 changed files with 8688 additions and 0 deletions

346
cognitive-load/SKILL.md Normal file
View File

@@ -0,0 +1,346 @@
---
name: cognitive-load
description: Measure how much mental effort a codebase demands from developers. Use when identifying hotspots, before architectural decisions, or when code feels hard to understand.
---
# Cognitive Load Analysis
## Overview
Cognitive load is the mental effort required to understand, modify, and reason about code. High cognitive load leads to bugs, slow development, and burnout.
This skill provides a framework for measuring and reducing cognitive load using the **Cognitive Load Index (CLI)**, scored 01000.
## CLI Score Rating Scale
| CLI Score | Rating | Action |
|-----------|--------|--------|
| 0100 | Excellent | Model codebase |
| 101250 | Good | Minor improvements |
| 251400 | Moderate | Address hotspots |
| 401600 | Concerning | Plan refactoring |
| 601800 | Poor | Significant refactoring needed |
| 801999 | Severe | Consider rewrite of affected areas |
## The 8 Dimensions
### D1: Structural Complexity
Cyclomatic complexity — the number of independent paths through code. Each `if`, `for`, `switch`, `case`, `&&`, `||` adds one.
**Thresholds for Go:**
- Simple function: < 5 (no issue)
- Moderate: 510 (watch closely)
- Complex: 1015 (consider refactoring)
- Critical: > 15 (must refactor)
**Detection:**
```bash
# Install: go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
gocyclo -over 10 ./...
```
**Go hotspots:** Request handlers that mix auth, validation, business logic, and response formatting in one function.
### D2: Nesting Depth
Maximum nesting depth — how many levels deep a reader must track.
**Thresholds for Go:**
- Ideal: 12 levels
- Acceptable: 3 levels
- Warning: 4 levels
- Critical: 5+ levels
**Fix:** Early returns and guard clauses eliminate deep nesting:
```go
// Bad: 4 levels deep
func process(r *http.Request) error {
if r != nil {
if r.Body != nil {
data, err := io.ReadAll(r.Body)
if err == nil {
// actual work at level 4
}
}
}
return nil
}
// Good: guard clauses, 1 level
func process(r *http.Request) error {
if r == nil {
return errors.New("nil request")
}
if r.Body == nil {
return errors.New("no request body")
}
data, err := io.ReadAll(r.Body)
if err != nil {
return fmt.Errorf("read body: %w", err)
}
// actual work at level 1
return nil
}
```
### D3: Volume/Size
Lines of code per file and function. Size alone isn't the issue — hidden responsibilities are.
**Thresholds for Go:**
- Function: > 50 lines is a signal (Go can go longer, but be honest about why)
- File: > 400 lines often means too many responsibilities
- Package: > 2000 lines total may need splitting
**Go note:** Table-driven tests inflate LOC legitimately. Distinguish test LOC from production LOC when assessing.
### D4: Naming Quality
Identifiers that don't reveal intent force readers to build a mental model from context.
**Poor naming signals:**
- Single-letter variables outside loop indices: `x`, `d`, `tmp`
- Generic suffixes: `Manager`, `Handler`, `Processor`, `Data`, `Info`, `Util`
- Abbreviations: `usrLst`, `custRepo`, `cfg2`
- Inconsistency: `getUserById`, `FetchCustomerByID`, `retrieveClientById` for the same concept
**Good naming in Go:**
```go
// Bad
func (m *Manager) proc(d interface{}) error { ... }
// Good
func (s *OrderService) PlaceOrder(ctx context.Context, req PlaceOrderRequest) (Order, error) { ... }
```
### D5: Coupling
How many packages/types a given unit depends on. High coupling means a change in one place ripples widely.
**Detection:**
```bash
# Count imports per file
grep -c "^import" *.go
# or examine import blocks manually
```
**Go-specific coupling smells:**
- A domain type that imports from `database/sql`, `net/http`, and `smtp` simultaneously
- A package that imports more than 10 other internal packages
- Circular imports (Go compiler rejects these, but near-circular is still a smell)
### D6: Cohesion
Whether things that belong together are together. Low cohesion means related code is scattered.
**Signs of low cohesion:**
- A function that calls many unrelated packages
- A package where functions don't share data or purpose
- Utility packages that grow without bound (`util`, `common`, `helpers`)
### D7: Duplication
Repeated logic that must be updated in multiple places when requirements change.
**Detection:**
```bash
# Simple: find identical error messages that suggest copy-paste
grep -r "error:" --include="*.go" | sort | uniq -d
```
**Go note:** Apply the Rule of Three — don't extract the first or second duplication. The third is the signal.
### D8: Navigability
How easily can a reader find relevant code and understand entry points?
**Signs of poor navigability:**
- No clear entry point (no `main.go`, no `New*` constructors)
- Package names that don't reflect content
- Files that mix many unrelated types
- Deep directory nesting with unclear boundaries
## Go-Specific Cognitive Load Hotspots
### Deeply Nested Error Handling
Go's explicit error handling is a feature, but it can create visual noise:
```go
// High cognitive load: multiple nested error paths
func (s *Service) Process(ctx context.Context, id string) error {
u, err := s.store.GetUser(ctx, id)
if err != nil {
if errors.Is(err, ErrNotFound) {
o, err := s.store.GetOrgByUserID(ctx, id)
if err != nil {
if errors.Is(err, ErrNotFound) {
return ErrEntityNotFound
}
return fmt.Errorf("get org: %w", err)
}
// ... more nesting
}
return fmt.Errorf("get user: %w", err)
}
// ... happy path
}
// Lower cognitive load: extract to focused functions
func (s *Service) Process(ctx context.Context, id string) error {
entity, err := s.resolveEntity(ctx, id)
if err != nil {
return fmt.Errorf("resolve entity: %w", err)
}
return s.processEntity(ctx, entity)
}
```
### Goroutine Soup
Goroutines without clear lifecycle management are high cognitive load:
```go
// High load: who owns these goroutines? When do they stop?
func startWorkers() {
go processQueue()
go cleanupExpired()
go sendNotifications()
}
// Lower load: explicit lifecycle with context and WaitGroup
func startWorkers(ctx context.Context) *WorkerPool {
pool := &WorkerPool{}
pool.wg.Add(3)
go pool.processQueue(ctx)
go pool.cleanupExpired(ctx)
go pool.sendNotifications(ctx)
return pool
}
func (p *WorkerPool) Shutdown() {
p.cancel()
p.wg.Wait()
}
```
### Over-Generalization with Generics
Generics add expressive power but also cognitive load. Use them sparingly:
```go
// High load: generic just because you can
func Transform[T any, U any](items []T, fn func(T) U) []U { ... }
// Lower load: concrete types when you have only one use case
func transformOrders(orders []Order) []OrderSummary { ... }
```
Extract generics only when you genuinely have multiple type applications.
### Init Functions
`init()` functions run invisibly and create hidden state:
```go
// High load: what order do these run? What state do they set?
func init() {
db = mustConnectDB()
cache = newCache()
}
// Lower load: explicit initialization in main or constructor
func main() {
db := mustConnectDB(cfg.DatabaseURL)
cache := newCache(cfg.CacheSize)
svc := NewService(db, cache)
// ...
}
```
### Long Interfaces
An interface with 10 methods forces implementors to understand all 10 methods before reasoning about any:
```go
// High cognitive load: 10 methods before you can implement
type UserRepository interface {
Create(ctx context.Context, u User) error
GetByID(ctx context.Context, id UserID) (User, error)
GetByEmail(ctx context.Context, email string) (User, error)
Update(ctx context.Context, u User) error
Delete(ctx context.Context, id UserID) error
List(ctx context.Context, filter Filter) ([]User, error)
Count(ctx context.Context, filter Filter) (int64, error)
Exists(ctx context.Context, id UserID) (bool, error)
Lock(ctx context.Context, id UserID) error
Unlock(ctx context.Context, id UserID) error
}
// Lower load: separate by use case
type UserReader interface {
GetByID(ctx context.Context, id UserID) (User, error)
GetByEmail(ctx context.Context, email string) (User, error)
}
type UserWriter interface {
Create(ctx context.Context, u User) error
Update(ctx context.Context, u User) error
Delete(ctx context.Context, id UserID) error
}
```
## When to Measure
- Before starting refactoring work: establish a baseline
- When a package/file is frequently the source of bugs
- When team members consistently report confusion about a subsystem
- Before a major feature addition that will touch complex code
## Hotspot Identification Process
1. **Size sweep:** Find files > 400 LOC and functions > 50 LOC
```bash
find . -name "*.go" ! -name "*_test.go" | xargs wc -l | sort -rn | head -20
```
2. **Complexity sweep:** Find high cyclomatic complexity
```bash
gocyclo -over 10 ./...
```
3. **Naming sweep:** Find packages with vague names
```bash
ls internal/ # Are there `util`, `common`, `helpers`?
```
4. **Coupling sweep:** Find files with many imports
```bash
grep -l "^import" **/*.go | xargs grep -c '"' | sort -t: -k2 -rn | head -10
```
5. **Duplication sweep:**
```bash
# Look for copied error strings, identical function signatures
grep -rn "TODO\|FIXME\|HACK" --include="*.go" # Technical debt markers
```
## Reduction Strategies
| High CLI Dimension | Primary Fix |
|-------------------|-------------|
| D1: Structural Complexity | Extract function, replace conditional with polymorphism |
| D2: Nesting Depth | Guard clauses, early returns |
| D3: Volume/Size | Extract type, split package |
| D4: Naming | Rename (most impactful, cheapest refactoring) |
| D5: Coupling | Introduce interface, apply DIP |
| D6: Cohesion | Extract type, reorganize package |
| D7: Duplication | Extract shared function (after Rule of Three) |
| D8: Navigability | Reorganize package structure, add package docs |
## Cross-References
- Load `refactoring` skill for systematic techniques to reduce identified hotspots
- Load `solid` skill for structural fixes (coupling, cohesion)
- Load `clean-code` skill for naming improvements