|
1 | 1 | # SPDX-License-Identifier: PLMP-1.0-or-later |
2 | | -# Dustfile template - recovery and rollback semantics |
| 2 | +# Dustfile — fine-grained recovery for echidnabot |
| 3 | +# |
| 4 | +# Cleanup of small leftover state: stale branches, orphan caches, |
| 5 | +# /tmp leftovers, dropped DB connections, half-completed migrations. |
| 6 | +# Run with: just dust-check (informational) or just dust-<name> (action) |
| 7 | +# Error-code namespace: D### |
3 | 8 |
|
4 | 9 | version: 1 |
5 | 10 |
|
6 | | -recovery: |
7 | | - logs: |
8 | | - - name: decision-log |
9 | | - path: logs/decisions.json |
10 | | - reversible: true |
11 | | - handler: "log-replay --reverse logs/decisions.json" |
12 | | - |
13 | | - policy: |
14 | | - - name: policy-rollback |
15 | | - path: policy/policy.ncl |
16 | | - rollback: "git checkout HEAD~1 -- policy/policy.ncl" |
17 | | - notes: "Rollback policy to the previous known-good revision." |
18 | | - |
19 | | - gateway: |
20 | | - - name: bad-deployment |
21 | | - event: "deploy.failure" |
22 | | - undo: "kubectl rollout undo deployment/gateway" |
23 | | - notes: "Undo a failed deployment while preserving audit logs." |
24 | | - |
25 | | - dust-events: |
26 | | - - name: decision-log-to-dust |
27 | | - source: logs/decisions.json |
28 | | - transform: "dustify --input logs/decisions.json --output logs/dust-events.json" |
29 | | - notes: "Map gateway decision logs into reversible dust events." |
| 11 | +dust-cadence: weekly (cheap) ; before-release (thorough) |
| 12 | + |
| 13 | +# ── Branch-Dust ──────────────────────────────────────────────────────── |
| 14 | + |
| 15 | +branches: |
| 16 | + - name: D001-stale-local-branches |
| 17 | + description: "Local branches whose remote-tracking ref is gone." |
| 18 | + severity: low |
| 19 | + probe: "git for-each-ref refs/heads --format '%(refname:short) %(upstream:track)' | grep -q '\\[gone\\]'" |
| 20 | + dust: "git fetch --prune; for b in $(git for-each-ref refs/heads --format='%(refname:short) %(upstream:track)' | awk '/gone/ {print $1}'); do gh pr list --repo hyperpolymath/echidnabot --head \"$b\" --state merged --json number --jq '.[0].number' | grep -q '^[0-9]' && git branch -D \"$b\"; done" |
| 21 | + reversible: yes |
| 22 | + |
| 23 | + - name: D002-stale-remote-branches |
| 24 | + description: "Remote branches whose PR was squash-merged." |
| 25 | + severity: low |
| 26 | + probe: "gh pr list --repo hyperpolymath/echidnabot --state all --limit 50 --json mergedAt | jq '[.[] | select(.mergedAt != null)] | length' | grep -qE '^[1-9]'" |
| 27 | + dust: "per merged PR: gh api -X DELETE repos/hyperpolymath/echidnabot/git/refs/heads/<branch>" |
| 28 | + |
| 29 | +# ── Worktree-Dust ────────────────────────────────────────────────────── |
| 30 | + |
| 31 | +worktrees: |
| 32 | + - name: D010-dangling-git-worktrees |
| 33 | + description: "Worktrees pointing at nonexistent paths." |
| 34 | + severity: low |
| 35 | + probe: "git worktree list 2>&1 | grep -q '\\[prunable\\]'" |
| 36 | + dust: "git worktree prune -v" |
| 37 | + reversible: yes |
| 38 | + |
| 39 | + - name: D011-tmp-agent-worktrees |
| 40 | + description: "/tmp/echidnabot-* leftover from prior agent runs." |
| 41 | + severity: low |
| 42 | + probe: "ls -d /tmp/echidnabot-* 2>/dev/null | wc -l | grep -qE '^[1-9]'" |
| 43 | + dust: "for d in /tmp/echidnabot-*; do git -C /home/hyperpolymath/developer/repos/echidnabot worktree remove --force \"$d\" 2>/dev/null || rm -rf \"$d\"; done" |
| 44 | + exclude: "active session work; the open #46 rsa branch worktree at /tmp/gitbot-tmp" |
| 45 | + |
| 46 | +# ── Cache-Dust ───────────────────────────────────────────────────────── |
| 47 | + |
| 48 | +caches: |
| 49 | + - name: D020-cargo-target-bloat |
| 50 | + description: "target/ directory exceeds 3GB." |
| 51 | + severity: low |
| 52 | + probe: "test -d target && du -sh target | awk '{print $1}' | grep -qE '^[3-9]G|^[1-9][0-9]+G$'" |
| 53 | + dust: "cargo clean" |
| 54 | + reversible: yes |
| 55 | + |
| 56 | + - name: D021-podman-image-orphans |
| 57 | + description: "Dangling podman images." |
| 58 | + severity: low |
| 59 | + probe: "podman images --filter dangling=true --format '{{.ID}}' | wc -l | grep -qE '^[1-9]'" |
| 60 | + dust: "podman image prune -f" |
| 61 | + reversible: yes |
| 62 | + |
| 63 | +# ── Process-Dust ─────────────────────────────────────────────────────── |
| 64 | + |
| 65 | +processes: |
| 66 | + - name: D030-orphan-pid-files |
| 67 | + description: "/tmp/*.pid pointing at dead PIDs." |
| 68 | + severity: low |
| 69 | + probe: "ls /tmp/echidnabot-*.pid 2>/dev/null | while read f; do kill -0 $(cat \"$f\") 2>/dev/null || echo \"$f\"; done | head -1" |
| 70 | + dust: "for f in /tmp/echidnabot-*.pid; do kill -0 $(cat \"$f\") 2>/dev/null || rm -f \"$f\" \"${f%.pid}.status\"; done" |
| 71 | + |
| 72 | +# ── DB-Dust ──────────────────────────────────────────────────────────── |
| 73 | + |
| 74 | +database: |
| 75 | + - name: D040-stale-migrations |
| 76 | + description: "migrations/ contains files newer than the DB's last-applied (sqlx)." |
| 77 | + severity: medium |
| 78 | + probe: "test -d migrations && ls migrations/*.sql 2>/dev/null | wc -l | grep -qE '^[1-9]'" |
| 79 | + dust: "sqlx migrate run (apply pending) ; OR sqlx migrate revert (rollback last)" |
| 80 | + reversible: "yes (sqlx migrate revert)" |
| 81 | + |
| 82 | + - name: D041-stale-sqlite-shm-wal |
| 83 | + description: "Stale -shm / -wal files alongside .db (sqlite ungraceful shutdown)." |
| 84 | + severity: low |
| 85 | + probe: "find . -name '*.db-shm' -o -name '*.db-wal' 2>/dev/null | head -1" |
| 86 | + dust: "rm -f *.db-shm *.db-wal (only after confirming no active sqlite process)" |
| 87 | + reversible: harmless |
| 88 | + |
| 89 | +# ── Source-Dust ──────────────────────────────────────────────────────── |
| 90 | + |
| 91 | +source: |
| 92 | + - name: D050-unused-imports |
| 93 | + description: "Rust unused imports detected by clippy." |
| 94 | + severity: low |
| 95 | + probe: "cargo clippy --workspace 2>&1 | grep -q 'unused import'" |
| 96 | + dust: "cargo fix --allow-dirty --allow-staged" |
| 97 | + reversible: yes |
| 98 | + |
| 99 | + - name: D051-todo-without-issue |
| 100 | + description: "TODO/FIXME comments without a tracking-issue reference." |
| 101 | + severity: low |
| 102 | + probe: "grep -rnE 'TODO|FIXME|XXX' src/ 2>/dev/null | grep -vE '#[0-9]+' | head -1" |
| 103 | + dust: "Per-comment: open tracking issue + add // TODO(#N): prefix, or close out the TODO." |
| 104 | + reversible: yes |
| 105 | + |
| 106 | + - name: D052-dead-code-warnings |
| 107 | + description: "Rust dead_code warnings." |
| 108 | + severity: low |
| 109 | + probe: "cargo build --workspace 2>&1 | grep -q 'warning: .* is never used'" |
| 110 | + dust: "Per-warning: keep if forward-prep (annotate #[allow(dead_code)] with reason), else remove." |
| 111 | + |
| 112 | +# ── Doc-Dust ─────────────────────────────────────────────────────────── |
| 113 | + |
| 114 | +docs: |
| 115 | + - name: D060-stale-changelog-unreleased |
| 116 | + description: "CHANGELOG.md [Unreleased] section has a dated marker > 30 days old." |
| 117 | + severity: low |
| 118 | + probe: "grep -E '^## \\[Unreleased\\] - ' CHANGELOG.md 2>/dev/null | head -1" |
| 119 | + dust: "Either cut a release (move Unreleased → [vX.Y.Z] - <date>) or remove the date from [Unreleased]." |
| 120 | + |
| 121 | +# ── Recovery primitives ──────────────────────────────────────────────── |
| 122 | + |
| 123 | +primitives: |
| 124 | + - name: source-rollback |
| 125 | + description: "Revert all unstaged source changes." |
| 126 | + command: "git checkout HEAD -- ." |
| 127 | + reversible: yes |
| 128 | + |
| 129 | + - name: branch-cleanup |
| 130 | + description: "Prune merged remote-tracking refs." |
| 131 | + command: "git fetch --prune" |
| 132 | + reversible: yes |
| 133 | + |
| 134 | +dust-philosophy: |
| 135 | + - "Idempotent: running dust on a clean repo is a no-op." |
| 136 | + - "Narrow blast radius: one action per recipe." |
| 137 | + - "Reversibility-first: every dust action undoable from git or by re-running a non-dust recipe." |
| 138 | + - "Exclusion list is sacred: active worktrees + production artefacts NEVER swept." |
0 commit comments