tools: add wiring-scan.sh dead-feature / orphan-wiring scanner for Pass N
This commit is contained in:
parent
954aab4cd2
commit
896691fee3
|
|
@ -0,0 +1,111 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# CloserApp — automated WIRING / dead-feature scanner (Pass N + discovery pre-check)
|
||||
#
|
||||
# Catches the "silent dead feature" class that QA found live (N-001 Bucket List, N-002
|
||||
# Date Builder): a feature looks like an empty/initial state but is actually non-functional
|
||||
# because a required id is never wired, a setter is never called, or saved data is never read.
|
||||
#
|
||||
# ⛔ CLAUDE: This is a LIVING tool — IMPROVE IT whenever you discover a new dead-wiring / orphan
|
||||
# failure mode. Add the new grep, keep the script self-contained + runnable from the project
|
||||
# root, write findings to stdout + SCAN_OUTPUT, and update this header with what you added.
|
||||
# Do not remove an existing check unless it is provably wrong (note why in the header).
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/wiring-scan.sh > /tmp/claude-wiring-scan-$(date +%Y%m%d).md
|
||||
# cat /tmp/claude-wiring-scan-$(date +%Y%m%d).md
|
||||
#
|
||||
# Tiers:
|
||||
# 🔴 CRITICAL — a `fun setX(...)` in a *ViewModel.kt with ZERO callers. The screen/nav never
|
||||
# pushes the value, so any op gated on it silently no-ops (N-001 class).
|
||||
# 🟠 MAJOR — a repository/data-source READ method (`fun observe*/get*/load*`) with no caller
|
||||
# in `ui/`. Data the app can write but never displays → orphan feature (N-002 class).
|
||||
# 🟡 REVIEW — a `if (x.isEmpty()/== null) return` / `?: return` bail-guard inside a *ViewModel.
|
||||
# Legitimate, BUT confirm something actually provides `x` — an un-provided guard is
|
||||
# exactly how a feature goes silently dead. Verify each by persisting real data and
|
||||
# reading it back from Firestore (admin), not by trusting the empty-state render.
|
||||
#
|
||||
# Findings are HINTS, not proofs — a flagged item can be intentional (e.g. a setter used only in a
|
||||
# @Preview, or a read method genuinely pending UI). Confirm each against live behavior + ground truth.
|
||||
#
|
||||
set -uo pipefail
|
||||
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SRC_DIR="$PROJECT_ROOT/app/src/main/java/app/closer"
|
||||
UI_DIR="$SRC_DIR/ui"
|
||||
SCAN_OUTPUT="${1:-/tmp/claude-wiring-scan-$(date +%Y%m%d).md}"
|
||||
: > "$SCAN_OUTPUT"
|
||||
|
||||
log() { echo "$1" | tee -a "$SCAN_OUTPUT"; }
|
||||
|
||||
crit=0; major=0; review=0
|
||||
|
||||
log "# Wiring / dead-feature scan — $(date '+%Y-%m-%d %H:%M')"
|
||||
log ""
|
||||
log "> Hints, not proofs. Confirm each against live behavior + a Firestore admin read."
|
||||
log ""
|
||||
|
||||
# ── Tier 1: dead setters (CRITICAL) ───────────────────────────────────────────
|
||||
log "## 🔴 CRITICAL — ViewModel \`setX(...)\` with no caller (N-001 class)"
|
||||
log ""
|
||||
while IFS= read -r decl; do
|
||||
file="${decl%%:*}"
|
||||
name="$(echo "$decl" | grep -oE 'fun set[A-Z][A-Za-z0-9]*' | head -1 | sed 's/^fun //')"
|
||||
[ -z "$name" ] && continue
|
||||
# callers = references to `name(` or `::name` anywhere under SRC, minus the declaration itself
|
||||
callers="$(grep -rEn "(\.|::| )${name}\(|::${name}\b" "$SRC_DIR" 2>/dev/null | grep -v "fun ${name}(" | grep -cv '^$')"
|
||||
if [ "${callers:-0}" -eq 0 ]; then
|
||||
log "- 🔴 \`${name}()\` — **no callers** — ${file#$PROJECT_ROOT/}"
|
||||
crit=$((crit+1))
|
||||
fi
|
||||
done < <(grep -rEn 'fun set[A-Z][A-Za-z0-9]*\(' "$SRC_DIR" --include=*ViewModel.kt 2>/dev/null)
|
||||
[ "$crit" -eq 0 ] && log "- none ✅"
|
||||
log ""
|
||||
|
||||
# ── Tier 2: orphan readers (MAJOR) ────────────────────────────────────────────
|
||||
log "## 🟠 MAJOR — repository/data-source read method never called from \`ui/\` (N-002 class)"
|
||||
log ""
|
||||
# Scan REPOSITORY INTERFACES only (*Repository.kt, which the glob excludes *RepositoryImpl.kt
|
||||
# + *DataSource.kt) — those are the read entry points a VM/screen would call. A repo read method
|
||||
# with no ui/ caller means the app can fetch the data but no screen ever shows it (N-002).
|
||||
while IFS= read -r decl; do
|
||||
file="${decl%%:*}"
|
||||
name="$(echo "$decl" | grep -oE 'fun (observe|get|load)[A-Z][A-Za-z0-9]*' | head -1 | sed 's/^fun //')"
|
||||
[ -z "$name" ] && continue
|
||||
ui_callers="$(grep -rEn "(\.|::| )${name}\(|::${name}\b" "$UI_DIR" 2>/dev/null | grep -cv '^$')"
|
||||
if [ "${ui_callers:-0}" -eq 0 ]; then
|
||||
log "- 🟠 \`${name}()\` — **no \`ui/\` caller** (written data may never be displayed) — ${file#$PROJECT_ROOT/}"
|
||||
major=$((major+1))
|
||||
fi
|
||||
done < <(grep -rEn 'fun (observe|get|load)[A-Z][A-Za-z0-9]*\(' "$SRC_DIR" --include=*Repository.kt 2>/dev/null)
|
||||
[ "$major" -eq 0 ] && log "- none ✅"
|
||||
log ""
|
||||
|
||||
# ── Tier 3: silent bail-guards in ViewModels (REVIEW) ─────────────────────────
|
||||
log "## 🟡 REVIEW — \`if (x.isEmpty()/==null) return\` / \`?: return\` bail-guards in ViewModels"
|
||||
log ""
|
||||
log "Each is a point where the feature silently does nothing if state was never initialized."
|
||||
log "Confirm something provides the value (persist real data → read it back via admin)."
|
||||
log "Auth-presence guards (\`currentUserId ?: return\`) are filtered out — they're never the wiring gap;"
|
||||
log "the risky ones gate on a STATE field a \`setX\`/resolver is supposed to populate (e.g. coupleId)."
|
||||
log ""
|
||||
while IFS= read -r hit; do
|
||||
log "- 🟡 ${hit#$PROJECT_ROOT/}"
|
||||
review=$((review+1))
|
||||
done < <(grep -rEn 'if \([a-zA-Z0-9_.]+\.(isEmpty\(\)|isBlank\(\))\) return|[a-zA-Z0-9_.]+ \?: return\b' "$SRC_DIR" --include=*ViewModel.kt 2>/dev/null \
|
||||
| grep -viE 'currentUser|currentUserId|authRepository|FirebaseAuth|firstOrNull|\.find ?\{|getOrNull')
|
||||
[ "$review" -eq 0 ] && log "- none"
|
||||
log ""
|
||||
|
||||
log "## Summary"
|
||||
log ""
|
||||
log "| Tier | Count |"
|
||||
log "|---|---|"
|
||||
log "| 🔴 CRITICAL (dead setters) | $crit |"
|
||||
log "| 🟠 MAJOR (orphan readers) | $major |"
|
||||
log "| 🟡 REVIEW (bail-guards) | $review |"
|
||||
log ""
|
||||
log "_Record these counts in ClaudeQACoverage.md under Pass N before driving the interactive features._"
|
||||
|
||||
# Exit non-zero only on CRITICAL so CI/automation can gate on it.
|
||||
[ "$crit" -gt 0 ] && exit 1 || exit 0
|
||||
Loading…
Reference in New Issue