2
0

chore(plugin): add plugin-shared drift-check script

Contract-verification approach: skill references/ files are curated
extracts (umlaut-transliterated, partial), so a content hash would
false-positive. Script verifies each extract declares an existing
plugin-shared master via its 'Master version:' footer and that any
named block resolves. Router full-mirror refs report MISSING-FOOTER
(informative, not drift). Test run: 22 checked, 0 drift, exit 0.
This commit is contained in:
Philipp Sieder
2026-05-21 22:40:54 +02:00
parent 276c7b24fc
commit f2634a7a0c
+116
View File
@@ -0,0 +1,116 @@
#!/usr/bin/env bash
set -euo pipefail
# drift-check.sh — Reference-drift detector for idea2self-skills
# =============================================================================
# ZWECK
# plugin-shared/ haelt die Master-Versionen der geteilten Referenzen
# (kommunikationsregeln.md, export-formate.md, methodik.md, recherche-regeln.md,
# artefakt-pipeline.md + templates/). Jeder Skill unter
# skills/<name>/references/ traegt KURATIERTE AUSZUEGE daraus — KEINE
# 1:1-Kopien. Die Auszuege sind bewusst umformuliert (Umlaut-Transliteration
# ue/oe/ae, gekuerzte Bloecke, skill-spezifische Zusatz-Bloecke). Dieses Script
# erkennt Drift, ohne diese gewollten Abweichungen als Fehler zu melden.
#
# GEWAEHLTER ANSATZ — Contract-Verifikation (NICHT Content-Diff)
# Ein naiver Hash- oder Section-Content-Vergleich wuerde wegen der
# Umformulierung (Umlaute, gekuerzte/umbenannte Bloecke) bei JEDER Datei
# Drift melden — unbrauchbar. Auch der Substring-pro-Zeile-Fallback scheitert,
# weil schon die Umlaut-Transliteration (z.B. "Huerden" im Auszug vs "Hürden"
# im Master) jede Zeile zum Fehlschlag bringt.
#
# Der einzige verlaessliche, maschinen-pruefbare Vertrag ist der Footer
# **Master version:** `plugin-shared/<datei>.md` [ (Block `<xml_tag>`) ]
# den die kuratierten Auszuege tragen. Das Script prueft diesen Vertrag:
# 1. Master-Pfad aus dem Footer existiert -> sonst DRIFT (broken pointer)
# 2. Optionaler "(Block `<tag>`)"-Verweis existiert im Master -> sonst DRIFT
# 3. Skill-Ref OHNE Footer -> MISSING-FOOTER (informativ,
# zaehlt NICHT als Drift; die klarheit-mit-idea2self-Refs sind bewusste
# Voll-Mirror ohne Footer-Vertrag).
#
# Damit faengt das Script die echte, handlungsrelevante Drift ab (Master
# umbenannt/geloescht, referenzierter Block aus Master entfernt) ohne
# Tausende False-Positives durch die gewollte Umformulierung.
#
# NUTZUNG
# bash plugin-shared/drift-check.sh
# Exit 0 = kein Drift, Exit 1 = Drift gefunden.
# =============================================================================
shopt -s nullglob
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(dirname "$SCRIPT_DIR")" # plugin-shared liegt direkt unter Repo-Root
command -v rg >/dev/null 2>&1 || true # rg optional; wir nutzen Bash-native Tools
SKILLS_DIR="$ROOT/skills"
if [[ ! -d "$SKILLS_DIR" ]]; then
echo "ERROR: skills directory not found at $SKILLS_DIR" >&2
exit 1
fi
checked=0
drift=0
# Sammle alle Skill-Reference-Dateien defensiv.
ref_files=()
if [[ -d "$SKILLS_DIR" ]]; then
while IFS= read -r f; do
ref_files+=("$f")
done < <(find "$SKILLS_DIR" -path '*/references/*.md' -type f 2>/dev/null | sort)
fi
for ref in "${ref_files[@]}"; do
[[ -e "$ref" ]] || continue
rel="${ref#"$ROOT"/}"
checked=$((checked + 1))
# Footer-Zeile extrahieren (erste Zeile mit "Master version:").
footer="$(grep -i "Master version" "$ref" 2>/dev/null | head -1 || true)"
if [[ -z "$footer" ]]; then
echo "MISSING-FOOTER $rel: kein 'Master version:'-Footer (Voll-Mirror? informativ, kein Drift)"
continue
fi
# Master-Pfad aus Backticks ziehen: erstes `plugin-shared/...md`.
master_rel="$(printf '%s\n' "$footer" \
| grep -oE 'plugin-shared/[A-Za-z0-9._/-]+\.md' | head -1 || true)"
if [[ -z "$master_rel" ]]; then
echo "DRIFT $rel: Footer vorhanden, aber kein plugin-shared/...md-Pfad parsbar"
drift=$((drift + 1))
continue
fi
master_abs="$ROOT/$master_rel"
if [[ ! -f "$master_abs" ]]; then
echo "DRIFT $rel: Master-Pfad existiert nicht: $master_rel"
drift=$((drift + 1))
continue
fi
# Optionalen Block-Verweis pruefen: (Block `<xml_tag>`)
block="$(printf '%s\n' "$footer" \
| grep -oE 'Block `<[A-Za-z0-9_]+>`' | head -1 || true)"
if [[ -n "$block" ]]; then
tag="$(printf '%s\n' "$block" | grep -oE '<[A-Za-z0-9_]+>' | head -1 || true)"
if [[ -n "$tag" ]]; then
if ! grep -qF "$tag" "$master_abs" 2>/dev/null; then
echo "DRIFT $rel: referenzierter Block $tag fehlt im Master $master_rel"
drift=$((drift + 1))
continue
fi
fi
fi
echo "OK $rel (master: $master_rel)"
done
echo "Checked $checked files, $drift drift(s)"
if [[ "$drift" -gt 0 ]]; then
exit 1
fi
exit 0