preen
Preen All
Perform a proactive, stateful pass through preen skills, implement focused improvements, open a PR, and enter the merge queue.
When to Run
Run this meta-skill regularly for continuous quality improvement. It supports broad sweeps in default mode and targeted incremental passes in rotating modes.
Self-Update Check (CRITICAL)
Before running, verify skill parity, command syntax, and registry drift:
- Run ecosystem checks:
./scripts/checks/preen/checkPreenEcosystem.ts --summary
- Ensure top-level preen docs are generated from registry:
./scripts/preen/generatePreenDocs.sh --check
If checks fail, STOP and sync before running preen:
./scripts/preen/generatePreenDocs.sh
./scripts/checks/preen/checkPreenEcosystem.ts --strict
Preen Skills Registry
| Skill | Purpose |
|---|---|
preen-typescript |
Fix weak TypeScript typings (any, as casts, @ts-ignore) |
preen-split-react-components |
Split oversized React components into smaller files |
preen-deferred-fixes |
Complete deferred follow-ups from merged PR reviews |
preen-optimize-test-execution |
Tune CI impact analysis (workflow filters, package dependencies) |
preen-database-performance |
Find and fix database performance issues (N+1 queries, inefficient joins, index gaps) |
preen-api-security |
Audit API for authorization, data access, and security issues |
preen-dependency-security |
Audit dependency vulnerabilities and unsafe versioning |
preen-test-flakiness |
Reduce flaky tests and nondeterministic waiting patterns |
preen-msw-parity |
Audit MSW handlers against API routes and improve test coverage assertions |
preen-skill-tooling |
Validate skills are wired into agentTool.ts and scriptTool.ts |
preen-skill-parity |
Ensure skill definitions are consistent across OpenCode, Codex, Gemini, and Claude |
preen-compliance-docs |
Audit compliance documentation for gaps and cross-framework parity |
preen-package-docs |
Audit and generate missing package README.md files |
preen-review-instructions |
Audit and update code review instructions (REVIEW.md, .gemini/INSTRUCTIONS.md) |
preen-i18n |
Audit i18n translation coverage, missing keys, and hardcoded strings |
preen-docs-internationalization |
Translate and sync documentation across all supported languages |
preen-window-consistency |
Normalize window components and standardize refresh patterns into window-manager |
preen-file-limits |
Break down large files exceeding project size limits (500 lines or 20,000 bytes) |
preen-knip |
Reduce knip findings by removing unused dependencies, exports, and files in coherent slices |
Run Modes
Use PREEN_MODE to control scope:
| Mode | Behavior |
|---|---|
full (default) |
Run all categories and land at most one fix per category |
single |
Run exactly one rotating category and land at most one fix |
security |
Run only security categories and land at most one fix |
audit |
Run discovery + scoring only; no edits, no branch, no PR |
Stateful Iteration (CRITICAL)
Persist rotation state locally so repeated runs in rotating modes naturally cover the full quality surface.
MODE="${PREEN_MODE:-full}"
STATE_DIR=".git/preen"
CURSOR_FILE="$STATE_DIR/cursor"
RUNS_FILE="$STATE_DIR/runs.jsonl"
CATEGORIES=(
"preen-typescript"
"preen-split-react-components"
"preen-deferred-fixes"
"preen-optimize-test-execution"
"preen-database-performance"
"preen-api-security"
"preen-dependency-security"
"preen-test-flakiness"
"preen-msw-parity"
"preen-skill-tooling"
"preen-skill-parity"
"preen-compliance-docs"
"preen-package-docs"
"preen-review-instructions"
"preen-i18n"
"preen-docs-internationalization"
"preen-window-consistency"
"preen-file-limits"
"preen-knip"
)
SECURITY_CATEGORIES=(
"preen-api-security"
"preen-dependency-security"
)
mkdir -p "$STATE_DIR"
[ -f "$CURSOR_FILE" ] || echo 0 > "$CURSOR_FILE"
CURSOR=$(cat "$CURSOR_FILE")
case "$MODE" in
single)
ACTIVE_CATEGORIES=("${CATEGORIES[$CURSOR]}")
NEXT_CURSOR=$(( (CURSOR + 1) % ${#CATEGORIES[@]} ))
MAX_FIXES=1
;;
full)
ACTIVE_CATEGORIES=("${CATEGORIES[@]}")
NEXT_CURSOR="$CURSOR"
MAX_FIXES=${#CATEGORIES[@]}
;;
security)
ACTIVE_CATEGORIES=("${SECURITY_CATEGORIES[@]}")
NEXT_CURSOR="$CURSOR"
MAX_FIXES=1
;;
audit)
ACTIVE_CATEGORIES=("${CATEGORIES[$CURSOR]}")
NEXT_CURSOR=$(( (CURSOR + 1) % ${#CATEGORIES[@]} ))
MAX_FIXES=0
;;
*)
echo "Unknown PREEN_MODE: $MODE"
exit 1
;;
esac
printf 'mode=%s active=%s max_fixes=%s\n' "$MODE" "${ACTIVE_CATEGORIES[*]}" "$MAX_FIXES"
Historical Analysis (Self-Improvement)
Before running discovery, analyze past runs to inform category selection and surface improvement opportunities:
analyze_history() {
[ ! -f "$RUNS_FILE" ] && return
[ "$(wc -l < "$RUNS_FILE" 2>/dev/null)" -lt 5 ] && return
# Categories with zero improvements in last 10 runs (stale)
STALE_CATEGORIES=$(jq -s '
[.[-10:][].selected_category] | group_by(.) |
map({cat: .[0], runs: length}) |
map(select(.runs > 2)) |
[.[].cat] - [
[.[-10:][] | select(.outcome == "changed") | .selected_category] | unique | .[]
]' "$RUNS_FILE" 2>/dev/null | jq -r '.[]' | tr '\n' ' ')
# Categories never selected (under-served) - computed efficiently with jq
ALL_CATEGORIES_JSON=$(printf '%s\n' "${CATEGORIES[@]}" | jq -R . | jq -s .)
UNDER_SERVED=$(jq -s --argjson all_cats "$ALL_CATEGORIES_JSON" \
'($all_cats - ([.[].selected_category] | unique)) | .[]' \
"$RUNS_FILE" 2>/dev/null | tr -d '"' | tr '\n' ' ')
# Success rate by category (effectiveness ranking)
EFFECTIVENESS=$(jq -s '
group_by(.selected_category) |
map({
cat: .[0].selected_category,
total: length,
success: ([.[] | select(.outcome == "changed")] | length),
rate: (([.[] | select(.outcome == "changed")] | length) / length * 100 | floor)
}) |
sort_by(-.rate)' "$RUNS_FILE" 2>/dev/null)
# Average improvement delta by category
AVG_DELTA=$(jq -s '
[.[] | select(.outcome == "changed")] |
group_by(.selected_category) |
map({
cat: .[0].selected_category,
avg_delta: ([.[].delta | tonumber] | add / length | floor)
}) |
sort_by(.avg_delta)' "$RUNS_FILE" 2>/dev/null)
}
analyze_history
Adaptive Category Selection
Use historical insights to adjust category priority:
compute_category_boost() {
local cat="$1"
local boost=0
# Boost under-served categories (+2)
if echo "$UNDER_SERVED" | grep -q -w "$cat"; then
boost=$((boost + 2))
fi
# Boost categories not run in last 5 iterations (+1)
RECENT=$(jq -s --arg c "$cat" '[.[-5:][].selected_category] | map(select(. == $c)) | length' "$RUNS_FILE" 2>/dev/null || echo 0)
if [ "$RECENT" -eq 0 ]; then
boost=$((boost + 1))
fi
# Penalize stale categories (-1)
if echo "$STALE_CATEGORIES" | grep -q -w "$cat"; then
boost=$((boost - 1))
fi
echo "$boost"
}
Integrate boost into candidate scoring:
adjusted_score = base_score + category_boost
base_score = (severity * 3) + (blast_radius * 2) + (confidence * 2) - effort
Workflow
0. Health Report (Self-Improvement)
Output a health report when sufficient history exists to guide focus areas:
if [ -f "$RUNS_FILE" ] && [ "$(wc -l < "$RUNS_FILE" 2>/dev/null)" -gt 5 ]; then
echo "=== Preen Health Report ==="
# Last run timestamp per category (read file once for efficiency)
echo "Category Coverage (last run):"
LAST_RUNS_JSON=$(jq -s 'group_by(.selected_category) | map({(.[0].selected_category): (last.timestamp)}) | add // {}' "$RUNS_FILE" 2>/dev/null)
for cat in "${CATEGORIES[@]}"; do
LAST=$(echo "$LAST_RUNS_JSON" | jq -r --arg c "$cat" '.[$c] // "never"')
printf " %-30s %s\n" "$cat:" "$LAST"
done
# Success rate per category (last 20 runs)
echo ""
echo "Success Rates (last 20 runs):"
jq -s '
.[-20:] | group_by(.selected_category) |
map(select(.[0].selected_category != null and .[0].selected_category != "")) |
map({
cat: .[0].selected_category,
rate: (([.[] | select(.outcome == "changed")] | length) / length * 100 | floor)
}) |
sort_by(-.rate) |
.[] | " \(.cat): \(.rate)%"' "$RUNS_FILE" 2>/dev/null || true
# Recommendations based on analysis
echo ""
echo "Recommendations:"
[ -n "$STALE_CATEGORIES" ] && echo " - Review stale categories (0 changes in 10 runs): $STALE_CATEGORIES"
[ -n "$UNDER_SERVED" ] && echo " - Prioritize under-served categories: $UNDER_SERVED"
# Check for consistently empty discovery
echo ""
echo "Discovery Health:"
for cat in "${ACTIVE_CATEGORIES[@]}"; do
METRIC=$(metric_count "$cat" 2>/dev/null || echo "error")
if [ "$METRIC" = "0" ]; then
echo " - WARNING: $cat discovery returns 0 findings - consider updating patterns"
fi
done
echo "==========================="
fi
1. Setup (Hardened)
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "Working tree is not clean. Commit or stash changes before running preen."
exit 1
fi
DEFAULT_BRANCH="$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name 2>/dev/null || true)"
if [ -z "$DEFAULT_BRANCH" ]; then
DEFAULT_BRANCH="$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || true)"
fi
if [ -z "$DEFAULT_BRANCH" ]; then
DEFAULT_BRANCH="main"
fi
git fetch origin "$DEFAULT_BRANCH"
git checkout "$DEFAULT_BRANCH"
git pull --ff-only origin "$DEFAULT_BRANCH"
2. Discovery (Active Categories Only)
Run discovery only for selected categories:
run_discovery() {
case "$1" in
preen-typescript)
rg -n --glob '*.{ts,tsx}'
': any|: any\[\]|<any>|as any|@ts-ignore|@ts-expect-error' .
| head -20
;;
preen-split-react-components)
find . -name '*.tsx'
-not -path '*/node_modules/*'
-not -path '*/.next/*'
-not -path '*/dist/*'
-exec wc -l {} \; 2>/dev/null
| awk '$1 > 300'
| sort -rn
| head -20
;;
preen-deferred-fixes)
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null);
gh issue list -R "$REPO"
--label 'deferred'
--state open
--limit 20 2>/dev/null || true
;;
preen-optimize-test-execution)
pnpm exec tsx scripts/ciImpact/ciImpact.ts
--base origin/main
--head HEAD
| head -40
;;
preen-database-performance)
rg -n --multiline --multiline-dotall --glob '*.{ts,tsx}'
'for\s*\([^)]*\)\s*\{.{0,400}?await\s+[^\n;]*db\.(select|query|execute)'
packages | head -40 || true
rg -n --glob '*.{ts,tsx}'
'\.(leftJoin|innerJoin|rightJoin|fullJoin|crossJoin)\('
packages | head -40 || true
rg -n --glob '**/*.{test,spec}.{ts,tsx}'
'withRealDatabase\(|createTestDatabase\('
packages | head -40 || true
;;
preen-api-security)
rg -n --glob '*.ts'
'router\.(get|post|put|patch|delete)|authClaims|req\.session|pool\.query|client\.query'
packages/api/src/routes | head -40
;;
preen-dependency-security)
pnpm audit --prod --audit-level high --json 2>/dev/null | head -40 || true
rg -n --glob 'package.json'
'latest|next|canary|beta'
packages scripts . | head -20 || true
;;
preen-test-flakiness)
rg -n --glob '**/*.{test,spec}.{ts,tsx}'
'setTimeout\(|waitForTimeout\(|sleep\('
packages . | head -30 || true
rg -n --glob '**/*.{test,spec}.{ts,tsx}'
'retry|retries|flaky|TODO.*flaky'
packages . | head -30 || true
;;
preen-msw-parity)
./scripts/checks/checkMswParity.ts
./scripts/checks/checkMswParity.ts --json | head -40
;;
preen-skill-tooling)
./scripts/checks/preen/checkPreenEcosystem.ts --summary
;;
preen-skill-parity)
./scripts/checks/skills/checkSkillParity.sh --summary
;;
preen-compliance-docs)
for fw in HIPAA NIST.SP.800-53 SOC2; do
echo "=== $fw ==="
echo "Policies: $(ls compliance/$fw/policies/*.md 2>/dev/null | wc -l | tr -d ' ')"
echo "Procedures: $(ls compliance/$fw/procedures/*.md 2>/dev/null | wc -l | tr -d ' ')"
echo "Controls: $(ls compliance/$fw/technical-controls/*.md 2>/dev/null | wc -l | tr -d ' ')"
done
find compliance -name '*.md'
-not -name 'POLICY_INDEX.md'
-not -name 'AGENTS.md'
| xargs -I{} basename {}
| grep -v '^[0-9][0-9]-'
| head -10
;;
preen-package-docs)
echo '=== Packages missing README ==='
for pkg in packages/*/; do
[ ! -f "${pkg}README.md" ] && basename "$pkg"
done
echo '=== Summary ==='
total=$(ls -d packages/*/ | wc -l | tr -d ' ')
with_readme=$(ls packages/*/README.md 2>/dev/null | wc -l | tr -d ' ')
echo "$with_readme/$total packages have READMEs"
;;
preen-review-instructions)
echo '=== REVIEW.md sections ==='
rg '^##' REVIEW.md | head -20
echo '=== Gemini sections ==='
rg '^##' .gemini/INSTRUCTIONS.md | head -20
echo '=== Section comparison ==='
echo "REVIEW.md: $(rg '^## ' REVIEW.md | wc -l | tr -d ' ')"
echo "Gemini: $(rg '^## ' .gemini/INSTRUCTIONS.md | wc -l | tr -d ' ')"
;;
preen-i18n)
echo '=== Translation Files ==='
find packages -path '*/i18n/translations/*.ts'
-not -name 'types.ts'
-not -name 'index.ts' | head -20
echo '=== Key Count by Language ==='
for lang in en es ua pt; do
count=$(rg -o "^\s+\w+:" packages/client/src/i18n/translations/${lang}.ts 2>/dev/null | wc -l | tr -d ' ')
echo "${lang}: ${count} keys"
done
echo '=== Potential Hardcoded Strings ==='
rg -n --glob '*.tsx'
'>\s*[A-Z][a-z]+(\s+[a-z]+)*\s*<'
packages | rg -v 'test\.' | head -20
;;
preen-docs-internationalization)
echo '=== Translation Coverage ==='
echo "English (source): $(ls docs/en/*.md 2>/dev/null | wc -l | tr -d ' ')"
echo "Spanish: $(ls docs/es/*.md 2>/dev/null | wc -l | tr -d ' ')"
echo "Ukrainian: $(ls docs/ua/*.md 2>/dev/null | wc -l | tr -d ' ')"
echo "Portuguese: $(ls docs/pt/*.md 2>/dev/null | wc -l | tr -d ' ')"
echo '=== Missing Translations ==='
for lang in es ua pt; do
for file in docs/en/*.md; do
target="docs/$lang/$(basename "$file")"
[ -f "$target" ] || echo "Missing: $target"
done
done
;;
preen-window-consistency)
rg -n --glob '*.tsx'
'lastRefreshTokenRef|lastRefreshToken'
packages | rg -v 'window-manager' | head -20
rg -n --glob '*.tsx'
'dragOverId.*useState|setDragOver.*Id'
packages | rg -v 'window-manager' | head -20
rg -n --glob '*.tsx'
'cursor-col-resize.*onMouseDown|handleResize.*MouseEvent'
packages | rg -v 'window-manager' | head -20
;;
preen-file-limits)
./scripts/checks/checkFileLimits.sh --all 2>&1 | head -40
;;
preen-knip)
pnpm exec knip --config knip.ts --use-tsconfig-files --reporter compact | head -80 || true
;;
esac
}
metric_count() {
case "$1" in
preen-typescript)
rg -n --glob '*.{ts,tsx}' ': any|: any\[\]|<any>|as any|@ts-ignore|@ts-expect-error' . | wc -l
;;
preen-split-react-components)
find . -name '*.tsx' -not -path '*/node_modules/*' -not -path '*/.next/*' -not -path '*/dist/*' -exec wc -l {} \; 2>/dev/null | awk '$1 > 300' | wc -l
;;
preen-deferred-fixes)
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null); gh issue list -R "$REPO" --label 'deferred' --state open --json number --jq 'length' 2>/dev/null || echo 0
;;
preen-optimize-test-execution)
pnpm exec tsx scripts/ciImpact/ciImpact.ts --base origin/main --head HEAD 2>/dev/null | jq '.warnings | length' 2>/dev/null || echo 0
;;
preen-database-performance)
rg -n --multiline --multiline-dotall --glob '*.{ts,tsx}' 'for\s*\([^)]*\)\s*\{.{0,400}?await\s+[^\n;]*db\.(select|query|execute)' packages | wc -l
;;
preen-api-security)
rg -L --glob '*.ts' 'authClaims|req\.session' packages/api/src/routes | rg -v 'index\.ts|shared\.ts|test\.' | wc -l
;;
preen-dependency-security)
pnpm audit --prod --audit-level high --json 2>/dev/null | jq '[.. | objects | .severity? // empty | select(. == "high" or . == "critical")] | length' 2>/dev/null || echo 0
;;
preen-test-flakiness)
rg -n --glob '**/*.{test,spec}.{ts,tsx}' 'setTimeout\(|waitForTimeout\(|sleep\(|retry|retries|flaky|TODO.*flaky' packages . | wc -l
;;
preen-msw-parity)
./scripts/checks/checkMswParity.ts --json | jq '.missingRouteCount + .lowConfidenceRouteCount'
;;
preen-skill-tooling)
./scripts/checks/preen/checkPreenEcosystem.ts --count-issues
;;
preen-skill-parity)
./scripts/checks/skills/checkSkillParity.sh --count-issues
;;
preen-compliance-docs)
gaps=0
for fw in HIPAA NIST.SP.800-53 SOC2; do
find "compliance/$fw/policies" -name '*-policy.md' -type f 2>/dev/null | while read -r policy_file; do
policy=$(basename "$policy_file" -policy.md)
[ ! -f "compliance/$fw/procedures/${policy}-procedure.md" ] && gaps=$((gaps + 1))
[ ! -f "compliance/$fw/technical-controls/${policy}-control-map.md" ] && gaps=$((gaps + 1))
done
done
unnumbered=$(find compliance -name '*.md' -not -name 'POLICY_INDEX.md' -not -name 'AGENTS.md' | xargs -I{} basename {} | grep -v '^[0-9][0-9]-' | wc -l)
echo $((gaps + unnumbered))
;;
preen-package-docs)
count=0; for pkg in packages/*/; do [ ! -f "${pkg}README.md" ] && count=$((count + 1)); done; echo $count
;;
preen-review-instructions)
GAPS=0
for section in 'TypeScript Standards' 'API Security' 'React Standards' 'Database Performance' 'Testing Standards'; do
[ -z "$(rg --fixed-strings -- "$section" REVIEW.md 2>/dev/null)" ] && GAPS=$((GAPS + 1))
done
REVIEW_SECTIONS=$(rg '^## ' REVIEW.md 2>/dev/null | wc -l | tr -d ' ')
GEMINI_SECTIONS=$(rg '^## ' .gemini/INSTRUCTIONS.md 2>/dev/null | wc -l | tr -d ' ')
[ "$GEMINI_SECTIONS" -lt $((REVIEW_SECTIONS / 2)) ] && GAPS=$((GAPS + 1))
echo $GAPS
;;
preen-i18n)
EN_KEYS=$(rg -o "^\s+\w+:" packages/client/src/i18n/translations/en.ts 2>/dev/null | wc -l | tr -d ' ')
GAPS=0
for lang in es ua pt; do
LANG_KEYS=$(rg -o "^\s+\w+:" packages/client/src/i18n/translations/${lang}.ts 2>/dev/null | wc -l | tr -d ' ')
[ "$LANG_KEYS" -lt "$EN_KEYS" ] && GAPS=$((GAPS + EN_KEYS - LANG_KEYS))
done
HARDCODED=$(rg -c --glob '*.tsx' '>\s*[A-Z][a-z]+(\s+[a-z]+)*\s*<' packages 2>/dev/null | rg -v 'test\.' | awk -F: '{sum+=$2} END {print sum+0}')
echo $((GAPS + HARDCODED))
;;
preen-docs-internationalization)
EN_COUNT=$(ls docs/en/*.md 2>/dev/null | wc -l | tr -d ' ')
GAPS=0
for lang in es ua pt; do
LANG_COUNT=$(ls docs/$lang/*.md 2>/dev/null | wc -l | tr -d ' ')
[ "$LANG_COUNT" -lt "$EN_COUNT" ] && GAPS=$((GAPS + EN_COUNT - LANG_COUNT))
done
echo $GAPS
;;
preen-window-consistency)
MANUAL_REFRESH=$(rg -c --glob '*.tsx' 'lastRefreshTokenRef|lastRefreshToken' packages 2>/dev/null | rg -v 'window-manager' | awk -F: '{sum+=$NF} END {print sum+0}')
MANUAL_DRAG=$(rg -c --glob '*.tsx' 'dragOverId.*useState|setDragOver.*Id' packages 2>/dev/null | rg -v 'window-manager' | awk -F: '{sum+=$NF} END {print sum+0}')
MANUAL_RESIZE=$(rg -c --glob '*.tsx' 'cursor-col-resize.*onMouseDown|handleResize.*MouseEvent' packages 2>/dev/null | rg -v 'window-manager' | awk -F: '{sum+=$NF} END {print sum+0}')
echo $((MANUAL_REFRESH + MANUAL_DRAG + MANUAL_RESIZE))
;;
preen-file-limits)
./scripts/checks/checkFileLimits.sh --all 2>&1 | grep "^ - " | wc -l
;;
preen-knip)
KNIP_JSON=$(mktemp)
pnpm exec knip --config knip.ts --use-tsconfig-files --reporter json > "$KNIP_JSON" 2>/dev/null || true
jq '[
.issues[]? |
(.dependencies // []),
(.devDependencies // []),
(.unlisted // []),
(.unresolved // []),
(.exports // []),
(.types // [])
] | flatten | length' "$KNIP_JSON" 2>/dev/null
rm -f "$KNIP_JSON"
;;
*)
echo 0
;;
esac
}
for category in "${ACTIVE_CATEGORIES[@]}"; do
run_discovery "$category"
done
3. Selection, Scoring, and Baseline
For each active category, select the highest-value candidate with this rubric:
score = (severity * 3) + (blast_radius * 2) + (confidence * 2) - effort
Selection guardrails:
single: maximum 1 total fix in the runfull: maximum 1 fix per active categorysecurity: maximum 1 security fixaudit: no fixes (discovery and scoring only)
Do not pick a candidate if confidence is low or behavior impact is unclear.
Capture baseline for the selected category:
SELECTED_CATEGORY="<set-after-scoring>"
SELECTED_CANDIDATE="<short-description>"
BASELINE_COUNT=0
if [ -n "${SELECTED_CATEGORY:-}" ]; then
BASELINE_COUNT=$(metric_count "$SELECTED_CATEGORY")
fi
4. Decision Gate and Branch Creation
Create a branch only after a candidate is selected and only for non-audit runs:
if [ "$MODE" = "audit" ] || [ -z "${SELECTED_CATEGORY:-}" ]; then
OUTCOME=$([ "$MODE" = "audit" ] && echo "audit" || echo "no-change")
echo "$NEXT_CURSOR" > "$CURSOR_FILE"
jq -nc \
--arg timestamp "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--arg mode "$MODE" \
--arg active "${ACTIVE_CATEGORIES[*]}" \
--arg selected "${SELECTED_CATEGORY:-}" \
--arg candidate "${SELECTED_CANDIDATE:-}" \
--arg baseline "$BASELINE_COUNT" \
--arg after "" \
--arg delta "" \
--arg outcome "$OUTCOME" \
--arg branch "" \
--arg pr "" \
'{timestamp:$timestamp,mode:$mode,active_categories:$active,selected_category:$selected,selected_candidate:$candidate,baseline_count:$baseline,after_count:$after,delta:$delta,outcome:$outcome,branch:$branch,pr:$pr}' >> "$RUNS_FILE"
echo "No code changes made. Active categories: ${ACTIVE_CATEGORIES[*]}"
exit 0
fi
BRANCH="refactor/preen-$(date +%Y%m%d-%H%M%S)"
git checkout -b "$BRANCH"
5. Implement Focused Fixes
Apply minimal, behavior-preserving changes and add/update tests where needed.
6. Validate
Run impacted checks first:
pnpm exec tsx scripts/ciImpact/runImpactedQuality.ts >/dev/null
pnpm exec tsx scripts/ciImpact/runImpactedTests.ts >/dev/null
Run full checks when the change is broad or security-sensitive:
pnpm typecheck >/dev/null
pnpm lint >/dev/null
pnpm test >/dev/null
CRITICAL: Verify coverage thresholds before proceeding.
Concurrent PR merges can cause cumulative coverage drops even when individual changes pass. Always verify coverage thresholds are maintained:
# Run coverage for packages with thresholds that may be affected
# Check which packages are impacted and run their coverage
# Note: Keep stdout visible here to see which packages are targeted
pnpm exec tsx scripts/ciImpact/runImpactedTests.ts
If coverage thresholds fail, DO NOT proceed. Add tests to bring coverage back above thresholds before continuing.
7. Quality Delta Gate
Before opening a PR, record measurable improvement. Example metrics:
- Type safety findings (
any, unsafe casts,@ts-ignore) - Oversized React files
- Deferred issue count
- CI impact warnings
- N+1 loop/query anti-pattern matches
- API security findings in touched area
- High/Critical dependency findings
- Flaky-pattern matches in tests
- MSW parity risk count (missing + low-confidence)
- Skill parity/tooling issues
- Cross-agent skill parity issues
- Compliance documentation gaps (missing triads + unnumbered files)
- Packages missing README.md
- Review instruction gaps (missing sections + Gemini drift)
- i18n gaps (missing translation keys + hardcoded strings)
- Missing or orphaned doc translations
- Non-standardized window patterns (manual refresh, drag, resize)
- Files exceeding size limits (500 lines or 20,000 bytes)
- Knip finding count (dependencies + devDependencies + unlisted + unresolved + exports + types)
Quality gate for the selected category:
AFTER_COUNT=$(metric_count "$SELECTED_CATEGORY")
if [ "$AFTER_COUNT" -ge "$BASELINE_COUNT" ]; then
echo "Quality delta gate failed: baseline=$BASELINE_COUNT after=$AFTER_COUNT"
exit 1
fi
8. Commit and Push
If changes were made:
git add -A
git commit -S -m "refactor(preen): stateful single-pass improvements" >/dev/null
git push -u origin "$BRANCH" >/dev/null
9. Open PR and Enter Merge Queue
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
PR_URL=$(gh pr create --repo "$REPO" --title "refactor(preen): stateful single-pass improvements" --body "$(cat <<'PR_BODY'
## Summary
- Ran stateful preen pass in `<mode>` mode
- Landed focused quality improvements with measurable delta
## Categories Checked
- [ ] TypeScript types (`any`, `as` casts, `@ts-ignore`)
- [ ] React component splitting
- [ ] Deferred fixes from PR reviews
- [ ] CI impact/test execution tuning
- [ ] Database performance (N+1, joins, indexes)
- [ ] API security boundaries
- [ ] Dependency/security hygiene
- [ ] Test flakiness hardening
- [ ] MSW/API parity and request-assertion wiring
- [ ] Skill tooling validation
- [ ] Skill parity across agents
- [ ] Compliance documentation gaps and parity
- [ ] Package documentation (README.md files)
- [ ] Review instruction completeness and sync
- [ ] i18n translation coverage and consistency
- [ ] Documentation internationalization coverage
- [ ] Window component consistency and refresh patterns
- [ ] Large files exceeding project limits
- [ ] Knip unused dependency/export cleanup
## Quality Delta
- [x] Baseline metric captured for selected category
- [x] Post-change metric captured for selected category
- [x] Metric improved (`after < baseline`)
## Test Plan
- [x] Impacted quality checks pass
- [x] Impacted coverage checks pass
- [x] Additional full checks run when needed
PR_BODY
)")
PR_NUMBER=$(echo "$PR_URL" | rg -o '[0-9]+$' || true)
/enter-merge-queue
10. Persist Cursor and Structured Run Log
Always persist rotation state and run metadata after evaluation:
OUTCOME="changed"
echo "$NEXT_CURSOR" > "$CURSOR_FILE"
jq -nc \
--arg timestamp "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--arg mode "$MODE" \
--arg active "${ACTIVE_CATEGORIES[*]}" \
--arg selected "${SELECTED_CATEGORY:-}" \
--arg candidate "${SELECTED_CANDIDATE:-}" \
--arg baseline "$BASELINE_COUNT" \
--arg after "$AFTER_COUNT" \
--arg delta "$((AFTER_COUNT - BASELINE_COUNT))" \
--arg outcome "$OUTCOME" \
--arg branch "$BRANCH" \
--arg pr "${PR_NUMBER:-}" \
'{timestamp:$timestamp,mode:$mode,active_categories:$active,selected_category:$selected,selected_candidate:$candidate,baseline_count:$baseline,after_count:$after,delta:$delta,outcome:$outcome,branch:$branch,pr:$pr}' >> "$RUNS_FILE"
No-Changes and Audit Cases
If no high-value candidate is found, or audit mode is used:
- Do not create a PR
- Still advance cursor in rotating modes (
single,audit) - Record structured run metadata in
.git/preen/runs.jsonl
Default-Mode Philosophy
Default full mode enables broad, category-by-category sweeps. Use single when you want smaller, lower-risk incremental changes that rotate over time.
Self-Improvement Protocol
The preen skill improves itself over time by analyzing run history and surfacing actionable recommendations.
Self-Modification Triggers
| Trigger | Condition | Action |
|---|---|---|
| Stale category | 0 changes in last 10 runs | Suggest removal or discovery refinement |
| Discovery drift | metric_count returns 0 |
Suggest updating discovery patterns |
| New anti-pattern | Manual fixes recur in uncovered area | Suggest new category proposal |
| High false-positive | Many audits, few changes (< 20% rate) | Suggest tightening discovery criteria |
| Under-served category | Never selected in recorded history | Boost priority or investigate blockers |
Periodic Evaluation (Every 10 Runs)
After every 10 preen runs, evaluate:
-
Stale Categories: If a category has 0 changes in 10 consecutive runs:
- Run discovery manually to verify patterns still exist in codebase
- If patterns exist but aren't being selected, adjust scoring weights
- If no patterns exist, consider removing category or updating discovery commands
-
Under-Served Categories: If coverage is uneven across categories:
- Check if some categories have expensive/slow discovery commands
- Consider splitting large categories into focused sub-categories
- Verify category isn't blocked by environmental issues
-
Registry Updates: When discovery patterns need adjustment:
- Update
scripts/preen/registry.jsonwith new discovery commands - Run
./scripts/preen/generatePreenDocs.shto regenerate skill docs - Test updated discovery with
PREEN_MODE=auditbefore full run
- Update
-
New Category Proposals: When deferred-fixes reveal recurring patterns:
# Find patterns in closed deferred-fix issues gh issue list --label deferred-fix --state closed --limit 50 --json title,body | jq -r '.[].title' | sort | uniq -c | sort -rn | head -10- If 3+ fixes cluster in same area, propose new preen category
- Draft discovery commands and metric count for the pattern
- Add to registry and regenerate docs
Run History Analysis
Query the run log to understand preen effectiveness:
# Overall success rate
jq -s '
{
total: length,
changed: ([.[] | select(.outcome == "changed")] | length),
no_change: ([.[] | select(.outcome == "no-change")] | length),
audit: ([.[] | select(.outcome == "audit")] | length)
} |
. + {success_rate: (.changed / .total * 100 | floor)}
' .git/preen/runs.jsonl
# Category leaderboard by impact
jq -s '
[.[] | select(.outcome == "changed")] |
group_by(.selected_category) |
map({
category: .[0].selected_category,
runs: length,
total_delta: ([.[].delta | tonumber] | add),
avg_delta: ([.[].delta | tonumber] | add / length | floor)
}) |
sort_by(.total_delta)
' .git/preen/runs.jsonl
# Time since last successful change per category
jq -s '
[.[] | select(.outcome == "changed")] |
group_by(.selected_category) |
map({category: .[0].selected_category, last_change: (last | .timestamp)}) |
sort_by(.last_change)
' .git/preen/runs.jsonl
When to Update This Skill
Update .claude/commands/preen.md when:
- Adding a new category: Add to
CATEGORIESarray,run_discoverycases,metric_countcases - Retiring a stale category: Remove from arrays after confirming no value
- Adjusting scoring: Modify
compute_category_boostor base scoring formula - Improving discovery: Update discovery commands for better signal-to-noise
- Adding security categories: Also add to
SECURITY_CATEGORIESarray
Always regenerate from registry after structural changes:
./scripts/preen/generatePreenDocs.sh
./scripts/checks/preen/checkPreenEcosystem.ts --strict
Guardrails
- In
singlemode, do not land more than one fix total - In
fullmode, do not land more than one fix per category - In
securitymode, do not land more than one security fix total - In
auditmode, do not make edits - Do not change runtime behavior unless fixing a bug
- Do not introduce new
any, unsafe casts, or@ts-ignore - Do not create empty PRs
- Keep each fix focused and independently verifiable
Token Efficiency
pnpm exec tsx scripts/ciImpact/runImpactedQuality.ts >/dev/null
pnpm exec tsx scripts/ciImpact/runImpactedTests.ts >/dev/null
pnpm typecheck >/dev/null
pnpm lint >/dev/null
pnpm test >/dev/null
git commit -S -m "message" >/dev/null
git push >/dev/null
On failure, re-run the failing command without suppression.