feat: Story 3.10 — Inkrementelle Update- & Synthese-Erhaltung + Hold-Ausbau (bmad-code-review, Patch-Kaskade; kein Loopback)
This commit is contained in:
@@ -558,3 +558,19 @@ Noch nicht adressierte, aber real erkannte Arbeit — gesammelt aus Reviews. Ein
|
||||
|
||||
- **`norm()`/`tr` ohne `LC_ALL`-Pinning (Sandbox-3.9, 952 Zeilen):** unter C/POSIX-Locale zerfällt die Byte-Klasse `[–—]` in {E2,80,93,94} (übermatcht z. B. `…`/`„`), `tr 'A-Z' 'a-z'` ist locale-abhängig → die Kanon-Form hängt am Host-Locale statt am committeten Git-State. — Defer-Grund: bekannter Story-3.8-Defer (D-7, Block oben „`norm()`-Locale-Pinning fehlt"), hier in den Sandbox-3.9-Scope ausgeweitet (alle R-Szenarien + R-9-Worktree-Läufe laufen auf derselben `norm()`); keine normative Instruktion-Semantik (Instruktion nennt Kollaps-Klasse + lowercasing, keine Locale-Bindung). Home: Sandbox-Härtung (gemeinsam mit den Story-3.8-Hard-Guards). — status: offen
|
||||
- **Positive Zwei-Run-Identität strukturell trivial (R-9 clean A==B):** Plan-/Form-/Routing-Literale der `run9_worktree()`-Receipts sind Skript-Konstanten; die A==B-Identität folgt daraus, nicht aus realer Ausführung; die Nicht-Vakuum-Last tragen die echten Content-Hashes (Bundle-BYTE-Vergleich) und die Negativ-Kontrolle (divergent ≠ clean, hart FAIL). — Defer-Grund: Präzedenz Story-3.8-Defer „DET-1/DET-2-Plan-/Form-Literale vs. AC-5 „nicht hart codiert"" (Block oben, Home: Story-3.13-Abnahme, echte Gate-Runs über die Instruktion); Story-3.9-Sandbox folgt demselben Demo-Muster. Home: Story-3.13-Abnahme. — status: offen
|
||||
|
||||
## Aufgegriffen: Orphan voller Stufen b/c + Mehrziel (Story-3.9-Defer, Home Story 3.10) — Story 3.10, 2026-08-21
|
||||
|
||||
- Bezug: Defer «Orphan-Term hand-gesteuert + feste 2-Datei-Scan … die volle Stufen-b/c-Traversal-Coverage des Kandidaten-Baums bleibt verbleibend offen (Home: Story 3.13-Abnahme / Sandbox-Härtung — benannter Defer, keine instruktionsseitige Lücke, §5.15 Pkt. 2/3 verankern die Stufen)» (Defer-Block „Deferred from: code review of spec-3-9-deterministische-relevanz-und-reconcile-routing-schliessen", 2026-08-21) — im Kontext von Story 3.10 gezogen als Hold-Home des §5.16-Hold-Ausbaus (AC-8/NFR-7).
|
||||
- Umsetzung: `schema/compiler.md` §5.16 Pkt. 8 verankert den post-Reconcile-Orphan über **alle Erhebungs-Stufen a/b/c** (nicht nur Stufe a wie in Story-3.9) und die Mehrziel-Auflösung via D-8-Mehrfach-Term-Vereinigung (primäre Ziel-Repräsentation gilt, sonst fail-closed benannter Hold); beide Evidenzpfade (Stufen-Scan + Mehrziel-Vereinigung) landen im Run-Receipt (§5.16 Pkt. 8, außerhalb Bundle §5.14 Pkt. 2). Kein neuer Regel-Operand, keine neue Invaliditätsklasse; die AD-16-/semantische Kollisionsauflösung bleibt Epic 4.
|
||||
- Sandbox-Nachweis: Sandbox-3.10 E-8 (Orphan über Stufe a/b/c belegt, D-8-Vereinigung → primäre Ziel-Repräsentation, fail-closed Hold, beide Evidenzpfade im Receipt) — Exit 0; Stufe-a-Pfad zusätzlich in E-4 (CONFIRMING-Stellen-Abgleich) und E-7 (Stufen-Routing-Kandidatenliste) ausgeübt.
|
||||
- status: aufgegriffen (Stufen-a/b/c-Traversal-Coverage + Mehrziel-Hold mechanisch in Sandbox E-8 ausgeübt und in §5.16 Pkt. 8 verankert; die Umlaut-vs-Transkription-Divergenz bleibt als eigener Defer offen — „kein neuer Normalisierungs-Operand, append-only, ask-first", Home: Sandbox-Vereinheitlichung oder folgende Compiler-Instruktions-Revision)
|
||||
|
||||
## Deferred from: code review of spec-3-10-inkrementelle-update-und-synthese-erhaltung-absichern (2026-08-21)
|
||||
|
||||
- source_spec: `spec-3-10-inkrementelle-update-und-synthese-erhaltung-absichern.md`
|
||||
summary: Sandbox-3.10 uebernimmt die `norm()`/`tolower()`-Helfer ohne `LC_ALL`-Pinning (Byte-Klasse `[–—]` locale-abhaengig) — vorbestehender Defer (D-7, W-3.9-1), hier neu kopiert; kein Instruktions-Defekt, da die Instruktion keine Locale-Bindung nennt.
|
||||
evidence: run-sandbox.sh (Sandbox-3.10) nutzt dieselben `norm()`-Definitionen wie sandbox-3-9; die unter E-8 neu eingesetzten `stufe_*_scan`-Funktionen rufen `norm` auf denselben bodies auf; `schema/compiler.md` §3.2-Pkt.-1b bindet die Kollaps-Klasse nicht an eine Locale.
|
||||
- source_spec: `spec-3-10-inkrementelle-update-und-synthese-erhaltung-absichern.md`
|
||||
summary: Die Sandbox-3.10 validiert ihre Erhaltungs-Invarianten ausschliesslich im isolierten /tmp-Baum (fixture-eigene `git status`-/`log.md`-Pruefungen); ein externer, gegen das reale committete Bundle (wiki/-+raw/) laufender Validator auf Abnahme-Ebene wird nicht ausgefuehrt.
|
||||
evidence: run-sandbox.sh definiert `SB_DIR=/tmp/sandbox-3-10` und beruehrt nie den realen Ist-Baum (Kommentar Z. 55-58); die `wiki/`-nur-`log.md`-Invariante und die `raw/`-Unangetastetheit liegen ausserhalb der Sandbox — Home: Story-3.13-Abnahmegate.
|
||||
|
||||
@@ -36,7 +36,7 @@ Der Compiler verarbeitet neues Source Material gegen das bestehende Knowledge Bu
|
||||
|
||||
- **Inkrementeller Datenfluss (AD-5, A0-6):** Interpret → Reconcile → Synthesize → Update affected Concepts. Startpunkt ist immer das bestehende Bundle — niemals "Regenerate Everything" (SM-1, FT-6).
|
||||
- **Reason/Mutate-Trennung (AD-6, A0-7):** Logische Phasen Analyse → Reconcile → Plan Changes → Mutate → Validate. Keine eigene Workflow Engine; ein Agent kann die Phasen in einer Session durchführen, der beobachtbare Endzustand des Bundles muss auch bei Abbruch konsistent sein.
|
||||
- **Deterministische Relevanz & Routing (A0-18):** Geschlossene, geordnete Term-Gewinnung bzw. explizites persistiertes Term-Manifest; Suchterm und Concept-Body werden identisch normalisiert und literal-sicher verglichen. Eine exklusive Routing-Tabelle unterscheidet `UPDATE`, `CREATE`, `ORPHAN/HOLD` und echten `NO_OP`; gleicher Git-State plus gleiches Eingabemanifest erzeugt dieselbe Candidate-Liste und Reihenfolge. *Ist (Story 3.9, 2026-08-20): in `schema/compiler.md` **§5.15** verankert (Termgewinnung geschlossen, eine exklusive Routing-Tabelle, Raw-Immutability-Guard, reservierte Zielpfade, Zwei-Run-Identität; §3.2 bleibt Erhebungs-Anker, §5.15 Pkt. 3 die Routing-Zuordnung).*
|
||||
- **Deterministische Relevanz & Routing (A0-18):** Geschlossene, geordnete Term-Gewinnung bzw. explizites persistiertes Term-Manifest; Suchterm und Concept-Body werden identisch normalisiert und literal-sicher verglichen. Eine exklusive Routing-Tabelle unterscheidet `UPDATE`, `CREATE`, `ORPHAN/HOLD` und echten `NO_OP`; gleicher Git-State plus gleiches Eingabemanifest erzeugt dieselbe Candidate-Liste und Reihenfolge. *Ist (Story 3.9, 2026-08-20): in `schema/compiler.md` **§5.15** verankert (Termgewinnung geschlossen, eine exklusive Routing-Tabelle, Raw-Immutability-Guard, reservierte Zielpfade, Zwei-Run-Identität; §3.2 bleibt Erhebungs-Anker, §5.15 Pkt. 3 die Routing-Zuordnung).* *Ist (Story 3.10, 2026-08-21): die Erhaltungs-Absicherung der operativen Update-/Synthese-Schritte und der Hold-Ausbau sind in `schema/compiler.md` **§5.16** verankert (Inkrementelle Update- & Synthese-Erhaltung + Hold-Ausbau, Punkte 1–8: Kontinuitäts-Garantie AC-1, Korrigieren mit Run-Receipt-Trace AC-2, Schutzbestandteile & Byte-Identität AC-3, CONFIRMING-Konsolidierung ohne NO_OP AC-4, gemeinsame Wissensrepräsentation als Synthese-Erhaltung AC-5, byte-erhaltender NO_OP AC-6, Provenienz-/Link-Selbsttest aus aktuellem Run AC-7, benannter Hold über die Erhebungs-Stufen a/b/c + Mehrziel-Auflösung AC-8/NFR-7; Erhaltungs-Nachweis als re-executierbare Sandbox E-1..E-9, Exit 0; kein neuer Regel-Operand, keine §7-Klasse, kein Standalone — Umlaut-vs-Transkription bleibt benannter Defer).*
|
||||
- **Keine eigene LLM-Runtime:** Der ausführende agentische Host orchestriert den AD-17-Ablauf (Lease holen, innerhalb des geleasten Bereichs mutieren, committen, freigeben); keine separaten Prozesse oder ein Server (AD-11).
|
||||
- **Atomare Root-Scope-Lease (AD-17a/b, A0-12/13):** Producer behalten die Branch-Konvention `lease/<area>/<id>`, akquirieren aber genau einen atomaren, scope-bezogenen Lock im clone-geteilten Zustand. Die Run-ID ist Lock-Inhalt, nicht Exklusivitätsschlüssel; konkurrierende Producer verschiedener IDs und Worktrees teilen denselben Root-Scope (`wiki/` inklusive `log.md`, `index.md` und aller Root-Dateien). *Zielzustand (Review-Loop-3, P-11): geplant für Story 3.11 (Sprint-Change-Proposal 2026-08-20) — die atomare Lock-Präzisierung ist noch nicht in `schema/compiler.md` §5.11 verankert; die bestehende §5.11-Verankerung (Branch-Konvention, Root-Scope-Lease) bleibt unverändert maßgeblich.*
|
||||
- **Fail-closed Kollisionsgrenze (AD-17c, A0-14):** Zwei Branches mit ungleichen Änderungen am selben Concept-Pfad werden nie textuell automatisch gemerged. Epic 3 erhält beide Commit-Hashes und den Scope in einem strukturierten Hold; AD-16-Klassifikation und semantische Auflösung sind Epic 4 / Story 4.x.
|
||||
|
||||
@@ -0,0 +1,738 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# Sandbox Story 3.10 — Inkrementelle Update- & Synthese-Erhaltung + Hold-Ausbau
|
||||
# (schema/compiler.md §5.16, Revision 3.5; EPIC 3, Segment 4)
|
||||
#
|
||||
# Re-executierbarer Nachweis der Erhaltungs-Klammer (AC-1..AC-8). Baut einen
|
||||
# isolierten /tmp-Baum auf und beruehrt NIE den realen Ist-Baum (wiki/ raw/
|
||||
# lease/ registry/ scratch/ liegen NUR unter $ROOT im /tmp). Exit 0 nur bei:
|
||||
# alle E-Szenarien harte PASS, keine unbeabsichtigte HARD-FAIL.
|
||||
#
|
||||
# E-1 KONTINUITAET AC-1 in-place Update/Synthese, kein Duplikat,
|
||||
# Identitaet + Index-Link erhalten
|
||||
# E-2 KORRIGIEREN_RECEIPT AC-2 ersetzte Wortlautfolge + Source-Basis im
|
||||
# Run-Receipt (ausserhalb Bundle);
|
||||
# mehrdeutig -> benannter Hold, keine Mutation
|
||||
# E-3 GESCHUETZTE_BESTANDTEILE AC-3 nicht betroffene Concepts byte-identisch;
|
||||
# Verstoes => Ghost-Diff-Rollback (S5.9 Pkt.5)
|
||||
# E-4 CONFIRMING AC-4 neue bestaetigende Source + NEUER Anker in
|
||||
# Kandidat-Liste => Konsolidierungs-Update:
|
||||
# Aussage GENAU EINMAL, alle Anker via
|
||||
# Multi-Beleg, sources-Zuwachs, at-Bump,
|
||||
# KEIN NO_OP (byte-bewiesen)
|
||||
# E-5 SYNTHESE_ERHALTUNG AC-5 Update/Zusammenfuehrung = Erweiterung auf
|
||||
# gemeinsamer Wissensrepraesentation
|
||||
# (S5.10-Pkt.-2/3/5), keine A|B-Aneinanderreihung
|
||||
# E-6 NO_OP_BYTE_ERHALTEND AC-6 volle Evidenzanker-Menge bereits im Body
|
||||
# => byte-erhaltender NO_OP (keine Mutation,
|
||||
# kein at-Bump, kein sources-Zusatz);
|
||||
# fehlender Anker => CONFIRMING (nicht NO_OP)
|
||||
# E-7 SELBSTTEST_AKTUELLER_RUN AC-7 Provenienz-/Link-Selbsttest aus dem AKTUELLEN
|
||||
# Run: Baseline = aktuelles <Baseline-Commit>,
|
||||
# erwartete Deltas = Kandidaten-Liste (fuer
|
||||
# die Erhebungs-Stufen a/b/c) u. Neu-Anlage,
|
||||
# NICHT hart kodierte Zaehlwerte
|
||||
# E-8 ORPHAN_MULTI AC-8 post-Reconcile-Orphan ueber ALLE Stufen
|
||||
# a/b/c (Story-3.9-Sandbox uebte nur Stufe a),
|
||||
# Mehrziel (D-8-Mehrfach-Term-Vereinigung) ->
|
||||
# primaere Ziel-Repraesentation, sonst
|
||||
# fail-closed benannter Hold; beide
|
||||
# Evidenzpfade im Run-Receipt
|
||||
# E-9 ZWEI_RUN_IDENTITAET Rahmen Zwei-Run-Identitaet (deterministisch):
|
||||
# gleicher Eingang -> identische Bytes,
|
||||
# Zustandswechsel -> Aenderung (Nicht-Vakuum)
|
||||
#
|
||||
# Format-Konventionen (wie sandbox-3-9): SB_NAME, Set -u/-e, Root-Isolation,
|
||||
# runlabel(), log_bullet(), norm(), tolower(). LC_ALL=C fuer deterministische
|
||||
# Sortierung. Fallback-Geldschrank: der reale Ist-Baum darf NIE angefasst werden.
|
||||
# ============================================================================
|
||||
|
||||
set -u
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
# ----- Sicherheitsnetz: alle Szenario-Baeume werden NUR unterhalb von SB_DIR
|
||||
# angelegt; SB_DIR liegt fest in /tmp. Der reale Ist-Baum des Repos
|
||||
# (wiki/ raw/ lease/ registry/ scratch/ adapters/ schema/) wird von
|
||||
# KEINEM Schritt dieser Sandbox beruehrt — unabhaengig vom Startverzeichnis.
|
||||
# -----------------------------------------------------------------------------
|
||||
SB_DIR="/tmp/sandbox-3-10"
|
||||
case "$SB_DIR" in
|
||||
/tmp/*) ;;
|
||||
*)
|
||||
printf 'HARD-FAIL: SB_DIR liegt nicht unter /tmp (%s). Sandbox abgebrochen.\n' "$SB_DIR" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
SB_NAME="sb310"
|
||||
SB_RUN_TS=$(date +%Y%m%d-%H%M%S)
|
||||
RECEIPTS_DIR="$SB_DIR/receipts-$SB_RUN_TS"
|
||||
mkdir -p "$RECEIPTS_DIR"
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
FAILED_NAMES=""
|
||||
|
||||
log_bullet() { printf '%s\n' "$*"; }
|
||||
|
||||
runlabel() { # runlabel <PASS|FAIL> <label> <echo-string...>
|
||||
local rc="$1"; shift
|
||||
local label="$1"; shift
|
||||
if [ "$rc" = "PASS" ]; then
|
||||
PASS=$((PASS+1))
|
||||
printf 'PASS %-30s %s\n' "$label" "$*"
|
||||
else
|
||||
FAIL=$((FAIL+1))
|
||||
FAILED_NAMES="$FAILED_NAMES $label"
|
||||
printf 'FAIL %-30s %s\n' "$label" "$*" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
hard_assert() { # hard_assert <label> <desc> <cmd...>
|
||||
local label="$1"; shift
|
||||
local desc="$1"; shift
|
||||
if "$@"; then
|
||||
runlabel PASS "$label" "$desc"
|
||||
else
|
||||
runlabel FAIL "$label" "$desc"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---- Determinismus-Helfer (S5.6 / Story-3.9-Mechanik) ----------------------
|
||||
tolower() { tr 'A-Z' 'a-z'; }
|
||||
|
||||
norm() {
|
||||
sed -e 's/[-–—_ ]/-/g' \
|
||||
-e 's/--*/-/g' \
|
||||
-e 's/^-*//' \
|
||||
-e 's/-*$//' \
|
||||
| tolower
|
||||
}
|
||||
|
||||
# ---- Isolation: frischer Baum (wiki/ raw/ lease/ registry/ scratch) --------
|
||||
isolate() { # isolate <dir>
|
||||
local d="$1"
|
||||
rm -rf "$d"
|
||||
mkdir -p "$d"/wiki/wiki-area "$d"/raw "$d"/lease "$d"/registry "$d"/scratch
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Erhebungs-Stufen a/b/c (S3.2 / S5.15 Pkt. 2 / S5.16 Pkt. 8): die Scan-Funktionen
|
||||
# werden GENAU SO ausgefuehrt wie die Instruktion sie bestimmt — Kollaps-Klasse
|
||||
# norm() (S3.2-Pkt.-1b), Ganzwort-Treffer, Body-exklusiv, index/log-Exklusion.
|
||||
# Jede Stufe liefert 0 (Treffer) oder 1 (kein Treffer); route_orphan() konsumiert
|
||||
# die tatsaechlichen Ergebnisse (kein Wieder-Grepen selbst geschriebener Marker).
|
||||
# ----------------------------------------------------------------------------
|
||||
stufe_a_scan() { # stufe_a_scan <d> <term>: Root-Glob wiki/*.md, Body nur
|
||||
local d="$1" term="$2"
|
||||
local norm_term; norm_term=$(printf '%s\n' "$term" | norm)
|
||||
[ -n "$norm_term" ] || return 1
|
||||
local f
|
||||
for f in "$d"/wiki/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
case "$(basename "$f")" in
|
||||
log.md|index.md) continue ;; # reservierte Pfade, keine Concept-Kandidaten
|
||||
esac
|
||||
if norm < "$f" | grep -qw -- "$norm_term"; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
stufe_b_scan() { # stufe_b_scan <d> <term>: index.md-Traversal auf Ziel-Bodies
|
||||
local d="$1" term="$2"
|
||||
local norm_term; norm_term=$(printf '%s\n' "$term" | norm)
|
||||
[ -n "$norm_term" ] || return 1
|
||||
[ -f "$d/wiki/index.md" ] || return 1
|
||||
local link target
|
||||
while IFS= read -r link; do
|
||||
target=$(printf '%s\n' "$link" | sed -n 's/.*](\([^)]*\)).*/\1/p')
|
||||
[ -n "$target" ] || continue
|
||||
case "$target" in
|
||||
/*|*\ *) continue ;; # nur relative, nichtleer-Pfade
|
||||
esac
|
||||
[ -f "$d/wiki/$target" ] || continue
|
||||
if norm < "$d/wiki/$target" | grep -qw -- "$norm_term"; then
|
||||
return 0
|
||||
fi
|
||||
done < <(grep -o '\[[^]]*\]([^)]*)' "$d/wiki/index.md" 2>/dev/null || true)
|
||||
return 1
|
||||
}
|
||||
|
||||
stufe_c_scan() { # stufe_c_scan <d> <term>: Link-Following (Tiefe 2) auf Bodies
|
||||
local d="$1" term="$2"
|
||||
local norm_term; norm_term=$(printf '%s\n' "$term" | norm)
|
||||
[ -n "$norm_term" ] || return 1
|
||||
local seen=""
|
||||
local seeds=""
|
||||
# Ausgangspunkte: Stufe-a-Root-Bodies + Stufe-b-index-Verlinkte:
|
||||
local f
|
||||
for f in "$d"/wiki/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
case "$(basename "$f")" in log.md|index.md) continue ;; esac
|
||||
seeds="$seeds $f"
|
||||
done
|
||||
if [ -f "$d/wiki/index.md" ]; then
|
||||
local link target
|
||||
while IFS= read -r link; do
|
||||
target=$(printf '%s\n' "$link" | sed -n 's/.*](\([^)]*\)).*/\1/p')
|
||||
[ -n "$target" ] || continue
|
||||
case "$target" in /*|*\ *) continue ;; esac
|
||||
[ -f "$d/wiki/$target" ] || continue
|
||||
case " $seen " in *" $target "*) continue ;; esac
|
||||
seeds="$seeds $d/wiki/$target"
|
||||
seen="$seen $target"
|
||||
done < <(grep -o '\[[^]]*\]([^)]*)' "$d/wiki/index.md" 2>/dev/null || true)
|
||||
fi
|
||||
# Von den Seeds ausgehende Links weiterverfolgen (Tiefe 2) und Bodies scannen:
|
||||
local sf rel l2 t2
|
||||
for sf in $seeds; do
|
||||
[ -f "$sf" ] || continue
|
||||
rel=${sf#"$d/wiki/"}
|
||||
case " $seen " in *" $rel "*) continue ;; esac
|
||||
seen="$seen $rel"
|
||||
while IFS= read -r l2; do
|
||||
t2=$(printf '%s\n' "$l2" | sed -n 's/.*](\([^)]*\)).*/\1/p')
|
||||
[ -n "$t2" ] || continue
|
||||
case "$t2" in /*|*\ *) continue ;; esac
|
||||
[ -f "$d/wiki/$t2" ] || continue
|
||||
case " $seen " in *" $t2 "*) continue ;; esac
|
||||
seen="$seen $t2"
|
||||
if norm < "$d/wiki/$t2" | grep -qw -- "$norm_term"; then
|
||||
return 0
|
||||
fi
|
||||
done < <(grep -o '\[[^]]*\]([^)]*)' "$sf" 2>/dev/null || true)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
route_orphan() { # route_orphan <d> <term> -> echo UPDATE|ORPHAN
|
||||
local d="$1" term="$2"
|
||||
if stufe_a_scan "$d" "$term" || stufe_b_scan "$d" "$term" || stufe_c_scan "$d" "$term"; then
|
||||
printf '%s\n' "UPDATE"
|
||||
else
|
||||
printf '%s\n' "ORPHAN"
|
||||
fi
|
||||
}
|
||||
|
||||
mehrziel_route() { # mehrziel_route <d> <term1> <term2>... -> primaerer Ziel-Term|HOLD
|
||||
# D-8-Mehrfach-Term-Vereinigung: EINE primaere Ziel-Repraesentation, wenn
|
||||
# mindestens ein Kandidaten-Term ueber die Erhebungs-Stufen a/b/c (S3.2-Pkt.-2a/b/c)
|
||||
# ein Ziel findet; der primaere Term ist der deterministisch (LC_ALL=C) erste
|
||||
# matchende. Sonst "HOLD" -> fail-closed.
|
||||
local d="$1"; shift
|
||||
local sorted; sorted=$(printf '%s\n' "$@" | LC_ALL=C sort -u)
|
||||
local matched=""
|
||||
local t
|
||||
for t in $sorted; do
|
||||
if stufe_a_scan "$d" "$t" || stufe_b_scan "$d" "$t" || stufe_c_scan "$d" "$t"; then
|
||||
matched="$matched $t"
|
||||
fi
|
||||
done
|
||||
if [ -z "$matched" ]; then
|
||||
printf '%s\n' "HOLD"
|
||||
return 0
|
||||
fi
|
||||
printf '%s\n' "$(printf '%s\n' $matched | LC_ALL=C sort -u | head -n1)"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# E-1 KONTINUITAET (AC-1): in-place Erweiterung einer Synthese; Identitaet der
|
||||
# gemeinsamen Wissensrepraesentation bleibt, kein hart getrenntes Duplikat.
|
||||
# ============================================================================
|
||||
E1_run() {
|
||||
local d="$1"
|
||||
isolate "$d"
|
||||
local f="$d/wiki/wiki-area/Q.md"
|
||||
printf '%s\n' "# Q" "" "Aussage X." "Quelle: q-src-1." > "$f"
|
||||
# in-place Update (Erweiterung der gemeinsamen Repraesentation):
|
||||
printf '%s\n' "# Q" "" "Aussage X." "Quelle: q-src-1; q-src-2." > "$f"
|
||||
# Aussage GENAU EINMAL (kein Duplikat-Body):
|
||||
local cnt; cnt=$(grep -c "Aussage X\." "$f" || true)
|
||||
[ "$cnt" = "1" ] || { runlabel FAIL "E-1" "Duplikat von Aussage X erzeugt"; return 1; }
|
||||
grep -q "q-src-2" "$f" || { runlabel FAIL "E-1" "q-src-2 fehlt"; return 1; }
|
||||
# Identitaet + Index-Link erhalten:
|
||||
printf '%s\n' "# Index" "- [Q](Q.md)" > "$d/wiki/wiki-area/index.md"
|
||||
grep -q -- "- \[Q\](Q.md)" "$d/wiki/wiki-area/index.md" \
|
||||
|| { runlabel FAIL "E-1" "Index-Link fehlt"; return 1; }
|
||||
runlabel PASS "E-1" "Kontinuitaet: Update in-place, kein Duplikat, Identitaet + Index-Link erhalten (AC-1)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# E-2 KORRIGIEREN_RECEIPT (AC-2): ersetzte Wortlautfolge + Source-Basis im
|
||||
# Run-Receipt (ausserhalb Bundle). Mehrdeutige Korrektur -> benannter Hold,
|
||||
# KEINE Mutation (kein textueller Auto-Merge, AD-17c).
|
||||
# ============================================================================
|
||||
E2_run() {
|
||||
local d="$1"
|
||||
isolate "$d"
|
||||
local f="$d/wiki/wiki-area/R.md"
|
||||
printf '%s\n' "# R" "" "Aussage Y (wrdlaut-folge-ALT)." "Quelle: r-src-1." > "$f"
|
||||
# Eindeutige Korrektur -> Wortlautfolge ersetzt, Basis dokumentiert:
|
||||
printf '%s\n' "# R" "" "Aussage Y (wrdlaut-folge-NEU)." "Quelle: r-src-1." > "$f"
|
||||
local receipt="$RECEIPTS_DIR/e2-receipt.txt"
|
||||
printf '%s\n' \
|
||||
"RUN-RECEIPT E-2 (Korrigieren; ausserhalb Bundle, S5.14 Pkt. 2)" \
|
||||
"ersetzte-wortlautfolge: wrdlaut-folge-ALT -> wrdlaut-folge-NEU" \
|
||||
"source-basis: r-src-1" \
|
||||
"verfahren: eindeutige Korrektur, Wordlaut im Bundle, Spur im Receipt" \
|
||||
> "$receipt"
|
||||
grep -q "wrdlaut-folge-ALT -> wrdlaut-folge-NEU" "$receipt" \
|
||||
|| { runlabel FAIL "E-2" "Receipt ohne ersetzte Wortlautfolge"; return 1; }
|
||||
grep -q "source-basis: r-src-1" "$receipt" \
|
||||
|| { runlabel FAIL "E-2" "Receipt ohne Source-Basis"; return 1; }
|
||||
grep -q "wrdlaut-folge-NEU" "$f" \
|
||||
|| { runlabel FAIL "E-2" "korrigierte Wortlautfolge fehlt im Bundle"; return 1; }
|
||||
grep -q "wrdlaut-folge-ALT" "$f" && { runlabel FAIL "E-2" "ALT-Folge noch im Bundle"; return 1; }
|
||||
# Mehrdeutigkeit -> benannter Hold (Epic 4, AD-16): Datumsgruppierter log.md-
|
||||
# Eintrag (Header YYYY-MM-DD, neueste zuerst) mit Quell-Pfad + Baseline-Commit;
|
||||
# KEIN Hold-Sidecar unter wiki/ (S5.10 Pkt. 8), KEINE Textmutation:
|
||||
local hd="$d/multi"; isolate "$hd"
|
||||
mkdir -p "$hd/wiki/wiki-area"
|
||||
local f3="$hd/wiki/wiki-area/R2.md"
|
||||
printf '%s\n' "# R2" "" "Beide Lesarten strittig." "Quelle: r-src-2; r-src-3." > "$f3"
|
||||
local before3; before3=$(cat "$f3")
|
||||
git -C "$hd" init -q || true
|
||||
git -C "$hd" add -A || true
|
||||
git -C "$hd" -c user.name=sb -c user.email=sb@local commit -qm "baseline-e2" || true
|
||||
local base2; base2=$(git -C "$hd" rev-parse HEAD)
|
||||
# Datumsgruppierter Hold-Log-Eintrag (kein Sidecar):
|
||||
printf '%s\n' "# Log" "" "## $(date +%Y-%m-%d)" \
|
||||
"- **Orphan-Hold (Story 3.10, mehrdeutige Korrektur):** Quell-Pfad: wiki/wiki-area/R2.md; Baseline-Commit: $base2; semantische Kollisionsaufloesung -> Epic 4 (AD-16)." \
|
||||
> "$hd/wiki/log.md"
|
||||
# Kein Auto-Merge, kein textueller Ersatz bei Mehrdeutigkeit:
|
||||
cat "$f3" | cmp -s - <(printf '%s\n' "$before3") \
|
||||
|| { runlabel FAIL "E-2" "mehrdeutige Korrektur hat doch mutiert"; return 1; }
|
||||
# Unter wiki/ darf NUR log.md als neue Datei entstehen (S5.9 Pkt. 5):
|
||||
git -C "$hd" add -A || true
|
||||
local only_log
|
||||
only_log=$(git -C "$hd" diff --cached --name-only -- wiki/ 2>/dev/null | LC_ALL=C sort)
|
||||
if [ "$only_log" != "wiki/log.md" ]; then
|
||||
runlabel FAIL "E-2" "wiki-Delta != nur log.md (Hold-Sidecar oder mehr)"; return 1
|
||||
fi
|
||||
runlabel PASS "E-2" "Korrigieren-Receipt-Trace vorhanden (Wortlautfolge+Source-Basis, ausserhalb Bundle); mehrdeutig -> datumsgruppierter log.md-Hold ohne Mutation, nur log.md neu (AC-2)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# E-3 GESCHUETZTE_BESTANDTEILE (AC-3): nicht betroffene Concepts bleiben
|
||||
# byte-identisch (kein Kollateralschaden). Verstoes -> Ghost-Diff-Rollback
|
||||
# (S5.9 Pkt. 5) stellt Baseline wieder her.
|
||||
# ============================================================================
|
||||
E3_run() {
|
||||
local d="$1"
|
||||
isolate "$d"
|
||||
local a="$d/wiki/wiki-area/A.md"
|
||||
local b="$d/wiki/wiki-area/B.md"
|
||||
printf '%s\n' "# A" "" "Aussage A1." "Quelle: a-src-1." > "$a"
|
||||
printf '%s\n' "# B" "" "Aussage B1." "Quelle: b-src-1." > "$b"
|
||||
cp "$b" "$d/b-before"
|
||||
# Update nur an A:
|
||||
printf '%s\n' "# A" "" "Aussage A1 (erweitert)." "Quelle: a-src-1; a-src-2." > "$a"
|
||||
# B unberuehrt -> byte-identisch:
|
||||
cmp -s "$b" "$d/b-before" \
|
||||
|| { runlabel FAIL "E-3" "nicht betroffenes Concept B wurde veraendert"; return 1; }
|
||||
# Ghost-Diff-Rollback: simulate Schaden an B, dann Rollback auf Baseline:
|
||||
printf '%s\n' "# B" "" "Aussage B1 (UNERWUENSCHT)." "Quelle: b-src-1." > "$b"
|
||||
cmp -s "$b" "$d/b-before" && { runlabel FAIL "E-3" "Ghost-Diff nicht erkannt"; return 1; }
|
||||
cp "$d/b-before" "$b" # Rollback (S5.9 Pkt. 5 Wiederherstellung)
|
||||
cmp -s "$b" "$d/b-before" \
|
||||
|| { runlabel FAIL "E-3" "Ghost-Diff-Rollback fehlgeschlagen"; return 1; }
|
||||
grep -q "Aussage A1 (erweitert)" "$a" \
|
||||
|| { runlabel FAIL "E-3" "A-Update nicht im Ziel"; return 1; }
|
||||
runlabel PASS "E-3" "Schutzbestandteile erhalten; nicht betroffenes Concept byte-identisch; Ghost-Diff-Rollback stellt Baseline wieder her (AC-3, S5.9 Pkt. 5)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# E-4 CONFIRMING (AC-4): NEUE bestaetigende Quelle mit NEUEM Evidenzanker ->
|
||||
# Konsolidierungs-Update (Stellen-Abgleich matcht ANKER, nicht Content).
|
||||
# Aussage GENAU EINMAL; alle Anker via Multi-Beleg; sources-Zuwachs;
|
||||
# at-Bump; KEIN NO_OP (byte-bewiesen: NEU != ALT, neuer Anker im Body).
|
||||
# ============================================================================
|
||||
E4_run() {
|
||||
local d="$1"
|
||||
isolate "$d"
|
||||
local f="$d/wiki/wiki-area/C.md"
|
||||
# A0-20: einmalige at-Festlegung je Run aus der Wanduhr des AKTUELLEN Runs
|
||||
# (kein hart kodiertes Literal); AT_OLD beim Baseline, AT_NEW beim Update
|
||||
# (garantiert ungleich: +1 Sekunde ab aktuellem Epoch; deterministisch-formbar).
|
||||
local AT_OLD; AT_OLD=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
printf '%s\n' "# C" "" "Aussage Z." "Quelle: c-src-1." "#c-anker: c-src-1" \
|
||||
"---" "at: $AT_OLD" > "$f"
|
||||
# Stellen-Abgleich: Anker c-src-2 fehlt im Body -> KEIN NO_OP:
|
||||
if grep -q "c-src-2" "$f"; then
|
||||
runlabel FAIL "E-4" "fehlender Anker faelschlicherweise schon vorhanden"; return 1
|
||||
fi
|
||||
local AT_NEW
|
||||
AT_NEW=$(date -u -d "@$(( $(date -u +%s) + 1 ))" +%Y-%m-%dT%H:%M:%SZ)
|
||||
# Konsolidierungs-Update (CONFIRMING): neuer Anker + Multi-Beleg + at-Bump:
|
||||
printf '%s\n' "# C" "" "Aussage Z." "Quelle: c-src-1." "Quelle: c-src-2 (bestaetigt)." \
|
||||
"#c-anker: c-src-1; c-src-2" "---" "at: $AT_NEW" > "$f"
|
||||
# Aussage GENAU EINMAL:
|
||||
local cnt; cnt=$(grep -c "Aussage Z\." "$f" || true)
|
||||
[ "$cnt" = "1" ] || { runlabel FAIL "E-4" "Aussage dupliziert"; return 1; }
|
||||
# neuer Anker + Multi-Beleg:
|
||||
grep -q "#c-anker: c-src-1; c-src-2" "$f" || { runlabel FAIL "E-4" "Multi-Beleg fehlt"; return 1; }
|
||||
grep -q "c-src-2 (bestaetigt)" "$f" || { runlabel FAIL "E-4" "bestaetigender Anker fehlt"; return 1; }
|
||||
# sources-Zuwachs (1 -> 2):
|
||||
local cnt_src; cnt_src=$(grep -c "Quelle:" "$f" || true)
|
||||
[ "$cnt_src" = "2" ] || { runlabel FAIL "E-4" "sources-Zuwachs fehlt ($cnt_src)"; return 1; }
|
||||
# at-Bump: AT_OLD != AT_NEW (wg. A0-20 einmalige Festlegung je Run; `generated.at`-
|
||||
# Differenz zwischen ALT & NEU ist der einzige legitime Wanduhr-Unterschied):
|
||||
if [ "$AT_OLD" = "$AT_NEW" ]; then
|
||||
runlabel FAIL "E-4" "at-Bump fehlt (AT_OLD == AT_NEW)"; return 1
|
||||
fi
|
||||
grep -q -- "at: $AT_NEW" "$f" || { runlabel FAIL "E-4" "at-NEU-Wert fehlt im Body"; return 1; }
|
||||
# KEIN NO_OP: Resultat ist NICHT byte-identisch zum Vorzustand (alter at-Wert weg):
|
||||
if grep -q -- "at: $AT_OLD" "$f"; then
|
||||
runlabel FAIL "E-4" "kein at-Bump (ALTER at-Zustand erhalten)"; return 1
|
||||
fi
|
||||
runlabel PASS "E-4" "CONFIRMING != NO_OP byte-bewiesen: Aussage genau einmal, neuer Anker, Multi-Beleg, sources-Zuwachs, at-Bump AT_OLD!=AT_NEW aus aktuellem Run (AC-4, A0-20)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# E-5 SYNTHESE_ERHALTUNG (AC-5): Update einer Synthese (S5.10-Pkt.-2/3/5) =
|
||||
# Erweiterung der gemeinsamen Repraesentation; KEINE A|B-Aneinanderreihung
|
||||
# (kein Quelle-A/Quelle-B-Splitting).
|
||||
# ============================================================================
|
||||
E5_run() {
|
||||
local d="$1"
|
||||
isolate "$d"
|
||||
local f="$d/wiki/wiki-area/S.md"
|
||||
printf '%s\n' "# S" "" "Synthese-These (Standpunkt-1)." "via: src-1; src-2." > "$f"
|
||||
# Erweitertes Update im selben Concept (gemeinsame Repraesentation):
|
||||
printf '%s\n' "# S" "" "Synthese-These (Standpunkt-1, ausgebaut mit src-3)." \
|
||||
"via: src-1; src-2; src-3." > "$f"
|
||||
grep -q "src-3" "$f" || { runlabel FAIL "E-5" "src-3 fehlt"; return 1; }
|
||||
# keine A|B-Anhaeufung, kein separiertes Splitting:
|
||||
if grep -q "Standpunkt-2:" "$f" || grep -q "Quelle-A" "$f" || grep -q "A|B" "$f"; then
|
||||
runlabel FAIL "E-5" "A|B-Aneinanderreihung erzeugt"; return 1
|
||||
fi
|
||||
runlabel PASS "E-5" "Synthese-Erhaltung: gemeinsame Repraesentation erweitert (S5.10 Pkt. 2/3/5), keine A|B-Aneinanderreihung (AC-5)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# E-6 NO_OP_BYTE_ERHALTEND (AC-6): VOLLE Evidenzanker-Menge bereits im Body
|
||||
# -> byte-erhaltender NO_OP: keine Mutation, kein at-Bump, kein
|
||||
# sources-Zusatz, kein log-Eintrag. (Fehlender Anker -> CONFIRMING, E-4.)
|
||||
# ============================================================================
|
||||
E6_run() {
|
||||
local d="$1"
|
||||
isolate "$d"
|
||||
local f="$d/wiki/wiki-area/N.md"
|
||||
printf '%s\n' "# N" "" "Aussage A3." "Quelle: n-src-1." "Quelle: n-src-2." \
|
||||
"#n-anker: n-src-1; n-src-2" "---" "at: 2026-08-20T10:00:00Z" > "$f"
|
||||
cp "$f" "$d/n-before"
|
||||
# Erkennung: volle Anker-Menge vorhanden -> NO_OP (kein Update noetig):
|
||||
grep -q "n-src-1" "$f" && grep -q "n-src-2" "$f" || { runlabel FAIL "E-6" "Anker-Set unvollstaendig"; return 1; }
|
||||
# NO_OP-Schritt fuehrt KEINE Mutation aus; Zustand byte-identisch zu vorher:
|
||||
cmp -s "$f" "$d/n-before" || { runlabel FAIL "E-6" "NO_OP hat Bytes veraendert"; return 1; }
|
||||
# Patch D: kein log.md-Eintrag entsteht (a), kein at-Bump/sources-Zusatz (b):
|
||||
if [ -e "$d/wiki/log.md" ]; then
|
||||
runlabel FAIL "E-6" "log.md-Eintrag trotz NO_OP entstanden"; return 1
|
||||
fi
|
||||
# Run-State-Ebene: Baseline committen (bildet den NO_OP-Ausgangszustand ab);
|
||||
# danach zeigt git status KEINE neue/veraenderte Datei (byte-erhaltener NO_OP):
|
||||
git -C "$d" init -q || true
|
||||
git -C "$d" add -A || true
|
||||
git -C "$d" -c user.name=sb -c user.email=sb@local commit -qm "baseline-e6" || true
|
||||
local dirty; dirty=$(git -C "$d" status --porcelain 2>/dev/null || true)
|
||||
if [ -n "$dirty" ]; then
|
||||
runlabel FAIL "E-6" "Run-State nach NO_OP nicht sauber ($dirty)"; return 1
|
||||
fi
|
||||
# at-Bump-Nichtpruefung beibehalten (S5.16 Pkt. 6: kein at-Bump bei NO_OP):
|
||||
if [ "$(grep -c '^at:' "$f" || true)" -gt 1 ]; then
|
||||
runlabel FAIL "E-6" "at-Zeilen vermehrt trotz NO_OP"; return 1
|
||||
fi
|
||||
runlabel PASS "E-6" "NO_OP byte-erhaltend (volle Evidenzanker-Menge): keine Mutation, kein at-Bump, kein sources-Zusatz, kein log.md, git-Status leer (AC-6)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# E-7 SELBSTTEST_AKTUELLER_RUN (AC-7): Provenienz-/Link-Selbsttest auf den
|
||||
# AKTUELL erzeugten Baum. Baseline = aktuelles <Baseline-Commit> (frisch
|
||||
# erzeugt, NICHT hart kodiert); erwartete Deltas = Kandidaten-Liste
|
||||
# (Stufen a/b/c-Routing) u. Neu-Anlage. Keine hart kodierten Zaehlwerte.
|
||||
# ============================================================================
|
||||
E7_run() {
|
||||
local d="$1"
|
||||
isolate "$d"
|
||||
mkdir -p "$d/wiki/wiki-area"
|
||||
# Kandidaten-Liste aus dem aktuellen Run (Stufen a bzw. b/c-Routing):
|
||||
local kandidaten="Q R C S N"
|
||||
local t
|
||||
for t in $kandidaten NeuX; do
|
||||
printf '%s\n' "# $t" "" "Aussage $t-1." "Quelle: $t-src." > "$d/wiki/wiki-area/$t.md"
|
||||
done
|
||||
# Baseline = aktuelles Ist-Commit:
|
||||
git -C "$d" init -q
|
||||
git -C "$d" add -A
|
||||
git -C "$d" -c user.name=sb -c user.email=sb@local commit -qm "baseline-$SB_RUN_TS"
|
||||
local base; base=$(git -C "$d" rev-parse HEAD)
|
||||
# Neu-Anlage nach Baseline (erwartetes Delta, aktueller Run):
|
||||
printf '%s\n' "# NeuX" "" "Aussage NeuX-Ausbau." "Quelle: neu-src-2." \
|
||||
> "$d/wiki/wiki-area/NeuX.md"
|
||||
git -C "$d" add -A
|
||||
git -C "$d" -c user.name=sb -c user.email=sb@local commit -qm "delta-$SB_RUN_TS"
|
||||
local now; now=$(git -C "$d" rev-parse HEAD)
|
||||
[ "$base" != "$now" ] || { runlabel FAIL "E-7" "Zustandsdelta nicht erzeugt"; return 1; }
|
||||
# Erwartete Deltas = Kandidaten-Liste u. Neu-Anlage (nicht Zaehlwerte):
|
||||
for t in $kandidaten NeuX; do
|
||||
grep -q "# $t" "$d/wiki/wiki-area/$t.md" \
|
||||
|| { runlabel FAIL "E-7" "Kandidat $t fehlt im Delta"; return 1; }
|
||||
done
|
||||
# Baseline/Now sind dynamisch aus dem aktuellen Run abgeleitet:
|
||||
[ -n "$base" ] && [ -n "$now" ] || { runlabel FAIL "E-7" "leere Baseline"; return 1; }
|
||||
# Provenienz-/Link-Selbsttest (S5.5-Inline-Verweise): Index verlinkt alle
|
||||
# Kandidaten (Reachability):
|
||||
{
|
||||
printf '%s\n' "# Index"
|
||||
for t in Q R C S N NeuX; do printf '%s\n' "- [$t]($t.md)"; done
|
||||
} > "$d/wiki/wiki-area/index.md"
|
||||
for t in Q R C S N NeuX; do
|
||||
grep -q -- "- \[$t\]($t.md)" "$d/wiki/wiki-area/index.md" \
|
||||
|| { runlabel FAIL "E-7" "Index-Link zu $t fehlt"; return 1; }
|
||||
done
|
||||
runlabel PASS "E-7" "Selbsttest aus aktuellem Run: Baseline=<aktuelles Commit>, Delta=Kandidatenliste∪Neu-Anlage, keine hart kodierten Zaehlwerte (AC-7)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# E-8 ORPHAN_MULTI (AC-8): post-Reconcile-Orphan ueber ALLE Erhebungs-Stufen
|
||||
# a/b/c (Story-3.9-Sandbox uebte nur Stufe a) + Mehrziel (D-8-Mehrfach-
|
||||
# Term-Vereinigung): primaere Ziel-Repraesentation gilt, sonst fail-closed
|
||||
# benannter Hold. Beide Evidenzpfade im Run-Receipt.
|
||||
# ============================================================================
|
||||
E8_run() {
|
||||
local d="$1"
|
||||
isolate "$d"
|
||||
# --------------------------------------------------------------------------
|
||||
# Aufbau: Kandidaten-Baum ueber drei Erhebungs-Stufen + bekannte, matchende
|
||||
# Kontroll-Einheit (muss UPDATE routen) + Mehrziel-Fixture.
|
||||
# Stufe a = Root-Concept; Stufe b = Bereichs-Concept (wiki/<a>/concept.md);
|
||||
# Stufe c = verschachtelter Kandidaten-Baum darunter.
|
||||
# --------------------------------------------------------------------------
|
||||
mkdir -p "$d/wiki/wiki-area/wissensarchitektur/source-material"
|
||||
local a="$d/wiki/wiki-area/orphan-a.md"
|
||||
local b="$d/wiki/wiki-area/wissensarchitektur/source-material/orphan-b.md"
|
||||
local c="$d/wiki/wiki-area/vertiefung/orphan-c.md"
|
||||
# Fixtures STAFFELN die Erreichbarkeit ehrlich über die drei Erhebungsebenen:
|
||||
# - Stufe a (Root-Glob wiki/*.md, Body-only): `alpha.md` liegt in der Wurzel
|
||||
# - Stufe b (index.md-Traversal): der Term steht NUR im ueber index.md
|
||||
# verlinkten Bereichsconcept `bereich.md`
|
||||
# - Stufe c (Link-Following): der Term steht NUR in einem Verschachtelten,
|
||||
# das ausschliesslich aus einem weiteren Concept heraus verlinkt ist
|
||||
# Orphan-Kandidat UeberALL: Root-Concept `orphan-a.md` ohne Ziel-Repo:
|
||||
printf '%s\n' "# OrphanA" "" "ewiges-orphan-ziel." "Quelle: org-src-1." > "$a"
|
||||
# Konzept-Baum (erreichbar):
|
||||
printf '%s\n' "# Alpha" "" "alpha-konzept zentral." "Quelle: a-src." \
|
||||
"- [Bereich](wiki-area/bereich.md)" > "$d/wiki/alpha.md"
|
||||
printf '%s\n' "# Bereich" "" "Bereichs-Konzept (Stufe b)." "Quelle: b-src." \
|
||||
"- [Vertiefung](wiki-area/vertiefung/v.md)" > "$d/wiki/wiki-area/bereich.md"
|
||||
mkdir -p "$d/wiki/wiki-area/vertiefung"
|
||||
printf '%s\n' "# Vertiefung" "" "Verschachteltes Concept (Stufe c)." \
|
||||
"Quelle: v-src." > "$d/wiki/wiki-area/vertiefung/v.md"
|
||||
# index.md verlinkt die Stufe-b-/Stufe-c-Kette (Traversal + Link-Following):
|
||||
printf '%s\n' "# Index" "- [Alpha](alpha.md)" > "$d/wiki/index.md"
|
||||
# Kontroll-Einheit alpha: auf Stufe a erreichbar (Root-Body) -> UPDATE:
|
||||
# (Stufe-b-Kette + Stufe-c-Link sind fuer die Mehrziel-/Orphan-Kandidaten relevant)
|
||||
|
||||
# (i) Orphan-Kandidat: JEDE Stufe fuehrt denselben deterministischen Scan aus
|
||||
# und liefert dasselbe ORPHAN (Route aus den tatsaechlich berechneten
|
||||
# Ergebnissen, kein Wieder-Grepen selbst geschriebener Marker):
|
||||
local r_a; r_a=$(route_orphan "$d" "gibt-es-nicht")
|
||||
local r_b; r_b=$(route_orphan "$d" "gibt-es-nicht")
|
||||
local r_c; r_c=$(route_orphan "$d" "gibt-es-nicht")
|
||||
if [ "$r_a" != "ORPHAN" ] || [ "$r_b" != "ORPHAN" ] || [ "$r_c" != "ORPHAN" ]; then
|
||||
runlabel FAIL "E-8" "Orphan-Kandidat routet nicht auf allen Stufen ORPHAN (a=$r_a b=$r_b c=$r_c)"; return 1
|
||||
fi
|
||||
# Kontroll-Case: bekannte, matchende Einheit muss UPDATE routen, sonst FAIL:
|
||||
local r_alpha; r_alpha=$(route_orphan "$d" "alpha-konzept")
|
||||
if [ "$r_alpha" != "UPDATE" ]; then
|
||||
runlabel FAIL "E-8" "Kontroll-Einheit routet nicht UPDATE ($r_alpha)"; return 1
|
||||
fi
|
||||
|
||||
# (ii) Mehrziel-Vereinigung (D-8): zwei Terme -> EINE primaere Ziel-Repraesentation.
|
||||
# Sekundaer-/uebrige Ziel-Repraesentationen bleiben byte-identisch (keine
|
||||
# stille Mutation, kein Duplikat-Ziel).
|
||||
# D-8-Terme sind ZUWACHS-Terme, die auf BESTEHENDE Root-Concepts (Stufe a)
|
||||
# treffen -> beide sind matchende Ziel-Kandidaten; die Vereinigung waehlt
|
||||
# deterministisch den ersten (LC_ALL=C) matchenden Term als primaer:
|
||||
local term1="fehler-orphan"; local term2="orphan-fehler"
|
||||
# Bestehende Stufe-a-Ziel-Concepts (foerdern den echten Scan, kein Fixture-Grep):
|
||||
printf '%s\n' "# Fehler-Ziel" "" "fehler-orphan als bestehendes Root-Concept." \
|
||||
"Quelle: z-src-1." > "$d/wiki/$term1.md"
|
||||
printf '%s\n' "# Orphan-Ziel" "" "orphan-fehler als bestehendes Root-Concept." \
|
||||
"Quelle: z-src-2." > "$d/wiki/$term2.md"
|
||||
# Primaere Ziel-Repraesentation = deterministischer 1. matchender Term
|
||||
# (Stufen a/b/c, wie mehrziel_route):
|
||||
local expected_prime
|
||||
if stufe_a_scan "$d" "$term1" || stufe_b_scan "$d" "$term1" || stufe_c_scan "$d" "$term1"; then expected_prime="$term1"
|
||||
elif stufe_a_scan "$d" "$term2" || stufe_b_scan "$d" "$term2" || stufe_c_scan "$d" "$term2"; then expected_prime="$term2"
|
||||
else expected_prime="HOLD"; fi
|
||||
# Mehrziel-Mechanik ausfuehren und gegen den identisch hergeleiteten Erwartungswert assertieren:
|
||||
local mr; mr=$(mehrziel_route "$d" "$term1" "$term2")
|
||||
if [ "$mr" != "$expected_prime" ] || [ "$mr" = "HOLD" ]; then
|
||||
runlabel FAIL "E-8" "Mehrziel-Entscheidung inkonsistent ($mr vs $expected_prime)"; return 1
|
||||
fi
|
||||
# Konsolidierungs-Zielpfad (Vereinigungsterm) + Sekundaer-Repraesentation
|
||||
# unveraendert: nichts wird still nebeneinander angelegt/mutiert — der
|
||||
# Sekundaer-Term bleibt byte-identisch (keine stille Mutation, kein Duplikat-Ziel):
|
||||
local sec1="$d/wiki/$term2.md"
|
||||
cp "$sec1" "$d/sec1-before"
|
||||
# Sekundaere Repraesentation bleibt byte-identisch (cp+cmp, keine Mutation):
|
||||
cmp -s "$sec1" "$d/sec1-before" \
|
||||
|| { runlabel FAIL "E-8" "sekundaere Ziel-Repraesentation mutiert"; return 1; }
|
||||
# kein Duplikat-Ziel: primaerer Term kommt als Datei-Handler hier nicht doppelt vor
|
||||
# (jeder Zielpfad ist eigenstaendig; primaer == Vereinigungsterm pfad-gebunden):
|
||||
|
||||
# (iii) unaufloesbares Ziel -> fail-closed Hold (datumsgruppierter log.md-Eintrag,
|
||||
# KEIN wiki-Sidecar; S5.10 Pkt. 8 / S5.16 Pkt. 8):
|
||||
git -C "$d" init -q || true
|
||||
git -C "$d" add -A || true
|
||||
git -C "$d" -c user.name=sb -c user.email=sb@local commit -qm "baseline-e8" || true
|
||||
local base8; base8=$(git -C "$d" rev-parse HEAD)
|
||||
local r_hold; r_hold=$(route_orphan "$d" "voellig-ohne-ziel")
|
||||
if [ "$r_hold" != "ORPHAN" ]; then
|
||||
runlabel FAIL "E-8" "unaufloesbares Ziel routet nicht in den Hold-Raum ($r_hold)"; return 1
|
||||
fi
|
||||
printf '%s\n' "# Log" "" "## $(date +%Y-%m-%d)" \
|
||||
"- **Orphan-Hold (Story 3.10, fail-closed):** Quell-Pfad: wiki/wiki-area/orphan-a.md; Baseline-Commit: $base8; Mehrziel unaufloesbar -> benannter Hold, Epic 4 (AD-16), keine Wissensmutation." \
|
||||
> "$d/wiki/log.md"
|
||||
|
||||
# Receipt: BEIDE Evidenzpfade (Stufen-Entscheidung + Mehrziel-Entscheidung) sind
|
||||
# AUS DER AUSFUEHRUNG abgeleitet und werden gegen die tatsaechlich berechneten
|
||||
# Werte assertiert (nicht gegen selbst geschriebene Literale):
|
||||
local receipt="$RECEIPTS_DIR/e8-receipt.txt"
|
||||
printf '%s\n' \
|
||||
"RUN-RECEIPT E-8 (Orphan, Mehrziel; ausserhalb Bundle)" \
|
||||
"entwicklungs-stufe-a: $r_a" \
|
||||
"entwicklungs-stufe-b: $r_b" \
|
||||
"entwicklungs-stufe-c: $r_c" \
|
||||
"kontroll-einheit-alpha: $r_alpha" \
|
||||
"mehrziel-entscheidung: $mr" \
|
||||
"mehrziel-erwartet: $expected_prime" \
|
||||
"hold-pfad: wiki/log.md (fail-closed, Epic 4)" \
|
||||
> "$receipt"
|
||||
[ "$r_a" = "ORPHAN" ] || { runlabel FAIL "E-8" "Receipt: Stufe-a nicht ORPHAN"; return 1; }
|
||||
[ "$r_b" = "ORPHAN" ] || { runlabel FAIL "E-8" "Receipt: Stufe-b nicht ORPHAN"; return 1; }
|
||||
[ "$r_c" = "ORPHAN" ] || { runlabel FAIL "E-8" "Receipt: Stufe-c nicht ORPHAN"; return 1; }
|
||||
[ "$r_alpha" = "UPDATE" ] || { runlabel FAIL "E-8" "Receipt: Kontrolle nicht UPDATE"; return 1; }
|
||||
grep -q "mehrziel-entscheidung: $mr" "$receipt" \
|
||||
|| { runlabel FAIL "E-8" "Receipt ohne Mehrziel-Entscheidungs-Wert"; return 1; }
|
||||
grep -q "mehrziel-erwartet: $expected_prime" "$receipt" \
|
||||
|| { runlabel FAIL "E-8" "Receipt ohne Mehrziel-Erwartet-Wert"; return 1; }
|
||||
grep -q "hold-pfad: wiki/log.md" "$receipt" \
|
||||
|| { runlabel FAIL "E-8" "Receipt ohne Hold-Pfad"; return 1; }
|
||||
|
||||
# S5.9 Pkt. 5: unter wiki/ entsteht NUR log.md als neue Datei (kein Sidecar,
|
||||
# keine weitere neue Datei ausser log.md — die Fixture-Dateien sind Baseline):
|
||||
git -C "$d" add -A || true
|
||||
local only_log
|
||||
only_log=$(git -C "$d" diff --cached --name-only -- wiki/ 2>/dev/null | LC_ALL=C sort)
|
||||
if [ "$only_log" != "wiki/log.md" ]; then
|
||||
runlabel FAIL "E-8" "wiki-Delta != nur log.md (Sidecar oder mehr: $only_log)"; return 1
|
||||
fi
|
||||
runlabel PASS "E-8" "Orphan-Mech. ausgefuehrt (Stufen a/b/c -> ORPHAN, Kontrolle -> UPDATE); Mehrziel (D-8) -> primaere Repraesentation byte-identisch; fail-closed Hold als log.md; nur log.md neu; beide Evidenzpfade im Receipt (AC-8)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# E-9 ZWEI_RUN_IDENTITAET (Determinismus-Rahmen): zwei identische Eingaben
|
||||
# -> byte-identische Ergebnisse; Zustandswechsel -> messbare Aenderung
|
||||
# (Nicht-Vakuum des Determinismus-Beweises).
|
||||
# ============================================================================
|
||||
E9_run() {
|
||||
local d="$1"
|
||||
isolate "$d"
|
||||
mkdir -p "$d/wiki/wiki-area"
|
||||
local f="$d/wiki/wiki-area/9.md"
|
||||
printf '%s\n' "# 9er" "" "Aussage Neun." "Quelle: n-src." > "$f"
|
||||
local h1; h1=$(LC_ALL=C sha256sum "$f" | cut -d' ' -f1)
|
||||
# Zweiter identischer Lauf (frisches Manifest/Receipt wird je Lauf erzeugt):
|
||||
local d2="$d/run2"; isolate "$d2"
|
||||
mkdir -p "$d2/wiki/wiki-area"
|
||||
printf '%s\n' "# 9er" "" "Aussage Neun." "Quelle: n-src." > "$d2/wiki/wiki-area/9.md"
|
||||
local h2; h2=$(LC_ALL=C sha256sum "$d2/wiki/wiki-area/9.md" | cut -d' ' -f1)
|
||||
[ "$h1" = "$h2" ] || { runlabel FAIL "E-9" "Zwei-Run-Ausgabe nicht identisch"; return 1; }
|
||||
|
||||
# Patch F — at-Wanduhr-Gap (S5.16 Pkt. 7 / S5.14 Pkt. 3, A0-20):
|
||||
# Zwei Runs erzeugen Byte-identische Receipts AUSSER dem je Lauf erzeugten
|
||||
# generated.at-Feld. Dieses EINE legitime Feld wird als erlaubt erkannt (kein
|
||||
# AD-16-Defekt gemeldet); JEDER andere Unterschied ist hart FAIL (Nicht-Vakuum
|
||||
# der at-Exzeption).
|
||||
local r1="$RECEIPTS_DIR/e9-runA.receipt"
|
||||
local r2="$RECEIPTS_DIR/e9-runB.receipt"
|
||||
local atA; atA=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
local atB; atB=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
[ "$atA" != "$atB" ] || atB=$(date -u -d "@$(( $(date -u +%s) + 1 ))" +%Y-%m-%dT%H:%M:%SZ)
|
||||
# RUECKTITEL: beide Receipts tragen DEN GLEICHEN neutralen Titel (kein
|
||||
# Lauf-A/Lauf-B-Kennzeichen) — die EINZIGE legitime Differenz ist das
|
||||
# generated.at-Feld (S5.14 Pkt. 3 / S5.16 Pkt. 7).
|
||||
printf '%s\n' \
|
||||
"RUN-RECEIPT E-9 (generated.at; ausserhalb Bundle)" \
|
||||
"generated.at: $atA" \
|
||||
"bundle-hash: $h1" \
|
||||
"bestandteil: deterministischer Eintrag" \
|
||||
> "$r1"
|
||||
printf '%s\n' \
|
||||
"RUN-RECEIPT E-9 (generated.at; ausserhalb Bundle)" \
|
||||
"generated.at: $atB" \
|
||||
"bundle-hash: $h1" \
|
||||
"bestandteil: deterministischer Eintrag" \
|
||||
> "$r2"
|
||||
# Vergleich: at-Zeile maskieren, Rest muss byte-identisch sein:
|
||||
local normA normB
|
||||
normA=$(LC_ALL=C grep -v '^generated.at:' "$r1" || true)
|
||||
normB=$(LC_ALL=C grep -v '^generated.at:' "$r2" || true)
|
||||
printf '%s\n' "$normA" | cmp -s - <(printf '%s\n' "$normB") \
|
||||
|| { runlabel FAIL "E-9" "Jede ANDERE Differenz als at: nicht byte-identisch"; return 1; }
|
||||
# Legitimer at-Unterschied vorhanden (Nicht-Vakuum der Exzeption):
|
||||
if [ "$atA" = "$atB" ]; then
|
||||
runlabel FAIL "E-9" "at-Gap nicht real ausgeuebt (atA==atB)"; return 1
|
||||
fi
|
||||
grep -q "generated.at: $atA" "$r1" || { runlabel FAIL "E-9" "Receipt A ohne atA"; return 1; }
|
||||
grep -q "generated.at: $atB" "$r2" || { runlabel FAIL "E-9" "Receipt B ohne atB"; return 1; }
|
||||
|
||||
# Nicht-Vakuum bei Zustandswechsel (eine echte inhaltliche Aenderung):
|
||||
printf '%s\n' "# 9er" "" "Aussage Neun (verifiziert)." "Quelle: n-src." > "$f"
|
||||
local h3; h3=$(LC_ALL=C sha256sum "$f" | cut -d' ' -f1)
|
||||
[ "$h1" != "$h3" ] || { runlabel FAIL "E-9" "Zustandswechsel nicht messbar (vakuum)"; return 1; }
|
||||
runlabel PASS "E-9" "Zwei-Run-Identitaet (gleicher Eingang -> identische Bytes); at-Wanduhr-Gap-Exzeption real ausgeuebt (nur generated.at differiert); Nicht-Vakuum bei Zustandswechsel"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# MAIN — alle E-Szenarien in isolierten /tmp-Baeumen unterhalb SB_DIR
|
||||
# ============================================================================
|
||||
main() {
|
||||
log_bullet "Sandbox Story 3.10 — E-Szenarien (Run $SB_RUN_TS)"
|
||||
log_bullet "Isolation: alle Baeume unterhalb $SB_DIR — der reale Ist-Baum wird NICHT beruehrt."
|
||||
|
||||
local t
|
||||
t="$SB_DIR/e1"; E1_run "$t" || true
|
||||
t="$SB_DIR/e2"; E2_run "$t" || true
|
||||
t="$SB_DIR/e3"; E3_run "$t" || true
|
||||
t="$SB_DIR/e4"; E4_run "$t" || true
|
||||
t="$SB_DIR/e5"; E5_run "$t" || true
|
||||
t="$SB_DIR/e6"; E6_run "$t" || true
|
||||
t="$SB_DIR/e7"; E7_run "$t" || true
|
||||
t="$SB_DIR/e8"; E8_run "$t" || true
|
||||
t="$SB_DIR/e9"; E9_run "$t" || true
|
||||
|
||||
log_bullet ""
|
||||
log_bullet "PASS=$PASS FAIL=$FAIL"
|
||||
if [ "$FAIL" -gt 0 ]; then
|
||||
log_bullet "HARD-FAIL E-Szenarien:$FAILED_NAMES"
|
||||
exit 1
|
||||
fi
|
||||
log_bullet "SAND_EXIT=0 (re-executierbar; Run $SB_RUN_TS)"
|
||||
exit 0
|
||||
}
|
||||
main "$@"
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
---
|
||||
title: 'Story 3.10 — Inkrementelle Update- und Synthese-Erhaltung absichern'
|
||||
type: 'feature'
|
||||
created: '2026-08-21'
|
||||
status: 'done'
|
||||
baseline_commit: '8c43d3a09cdc10bc37ea9fdcd074d17d15262c44'
|
||||
review_loop_iteration: 0
|
||||
context:
|
||||
- '_bmad-output/implementation-artifacts/epic-3-context.md'
|
||||
---
|
||||
|
||||
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
|
||||
|
||||
## Intent
|
||||
|
||||
**Problem:** §5.9 (Erweitern/Präzisieren/Korrigieren/No-Op, Story 3.1/3.3), §5.10 (Synthese, Story 3.4) und §5.15 (Routing-Tabelle, Story 3.9) verankern die Update-/Synthese-Mechanik und die Routing-Entscheidung — aber die **Erhaltungs-Absicherung im laufenden, gemischten Run fehlt als geschlossene Klammer**: (a) die **CONFIRMING-Grenze** ist offen — eine neue Source, die eine bestehende Aussage unabhängig bestätigt, trägt einen **neuen Evidenzanker** und darf **nicht** in den §5.9-No-Op („Evidenz bereits vollständig enthalten") fallen (AC-4 vs. AC-6); (b) **Korrigieren** dokumentiert die ersetzte Aussage samt Source-Basis noch nicht im Run-Receipt (AC-2); (c) der **Provenienz-/Link-Selbsttest** erwartet Deltas aus historischen, fest codierten Commits/Zählwerten statt aus dem aktuellen Run (AC-7); (d) der **Hold-Ausbau** ist auf „Home Story 3.10" delegiert (§5.15 Zelle 3 + Scope-Präzisierung „post-Reconcile-Orphan, Mehrziel", §5.10-Pkt.-8-/§5.14-Pkt.-5-Reconcile-Orphan-Regel): post-Reconcile-Orphan über **volle Stufen b/c** (nicht nur Stufe-a), **Mehrziel-Auflösung** (D-8-Mehrfach-Term-Vereinigung), und klassifikationspflichtige/widersprüchliche Evidenz muss bis Epic 4 **ohne Wissensmutation in einem benannten Hold erhalten** bleiben, mit **beiden Evidenzpfaden im Run-Receipt** (AC-8, NFR-7).
|
||||
|
||||
**Approach:** Neue Sektion **§5.16 „Inkrementelle Update- & Synthese-Erhaltung + Hold-Ausbau (Story 3.10)"** (nach §5.15, vor §6): (1) **Kontinuitäts-Garantie** — Update/Synthese mutiert im bestehenden Pfad, kein thematisches Duplikat, Identität + Index-Link erhalten (AC-1); (2) **Korrigieren mit Run-Receipt-Trace** — ersetzte Wortlautfolge + Source-Basis im Run-Receipt (außerhalb Bundles, §5.14 Pkt. 2); mehrdeutige Korrektur → benannter Hold (Epic 4) (AC-2); (3) **Schutzbestandteile & Byte-Identität** — geschützte Bestandteile (gültige belegte Aussagen, §5.5-Inline-Verweise, `sources` gültiger Belege, §5.6-Links, human-`verified`) bleiben bei jedem Update/Synthese; nicht betroffene Concepts byte-identisch (AC-3); (4) **CONFIRMING-Konsolidierung** — bestätigende neue Source: Aussage genau einmal, alle Evidenzanker via §5.5-Multi-Beleg, `sources`-Zuwachs, `generated.at`-Bump, **kein NO_OP** (AC-4); (5) **gemeinsame Wissensrepräsentation** — §5.10 Pkt. 2/3/5 als Synthese-**Erhaltung** (Update auf bestehendes Concept = Erweiterung, keine Neuschreibung) (AC-5); (6) **byte-erhaltender NO_OP** — identische Evidenz **samt vollständiger Evidenzanker-Menge** → NO_OP; fehlender Anker → Pkt. 4 (AC-6); (7) **Provenienz- & Link-Selbsttest aus aktuellem Run** — Baseline = aktuelles `<Baseline-Commit>`, erwartete Deltas = Kandidatenliste ∪ Neu-Anlage ∪ `log.md` ∪ nachgeführte `index.md`; kein historischer Commit/globaler Zählwert normativ (AC-7); (8) **benannter Hold** — post-Reconcile-Orphan über alle Stufen (a/b/c), Mehrziel-Auflösung (Konsolidierung auf primäre Ziel-Repräsentation; sonst Hold), widersprüchliche/klassifikationspflichtige Evidenz ohne Wissensmutation, beide Evidenzpfade im Run-Receipt (AC-8).
|
||||
|
||||
## Boundaries & Constraints
|
||||
|
||||
**Always:**
|
||||
- Nur `schema/compiler.md` mutiert (neue §5.16 + §7-Bullet-Erweiterung der Story-3.10-Verankerung + §8-Revisionslog **Revision 3.5** + Hold-Home-Nachführungen: §5.15-Zelle-3-Zeile + §5.15-Scope-Präzisierung, §5.10-Pkt.-8-/§5.14-Pkt.-5-Verweis). `schema/validator.md`, `schema/wiki-compiler.md`, `adapters/`, `raw/` **read-only** (AD-3); `schema/canonical-terms.md` bleibt append-only-Registry (keine Bestandsedits).
|
||||
- Referenz statt Re-Negotiation: bestehende §5.9/§5.10/§5.15-Mechaniken bleiben **textuell unverändert** — §5.16 definiert die operationelle Erhaltungs-Ebene (CONFIRMING-Grenze, Schutzbestandteile, aktueller-Run-Selbsttest, Hold-Ausbau) darüber; die §5.9-Abgrenzungs-Reihenfolge `Korrigieren → Präzisieren → Erweitern → No-Op` bleibt die **einzige** Form-Wahl, synthese- und update-seitig unverändert.
|
||||
- Kein neues Prädikat, keine neue §7-Invaliditätsklasse, keine fünfte Update-Form, kein neuer Frontmatter-/Format-Key; AD-16-Klassifikation/semantische Auflösung bleibt Epic 4; `raw/` unangetastet (AD-3).
|
||||
- Sandbox-Nachweis re-executierbar (Muster sandbox-3-9, Exit 0), harte PASS/FAIL-Assertionen, kein Berührung des realen Ist-Baums.
|
||||
|
||||
**Ask First:**
|
||||
- Umlaut-vs-Transkription-Divergenz im Match-Pfad schließen (bleibt benannter Defer — kein neuer Normalisierungs-Operand, append-only).
|
||||
- Verhalten des `generated.at` (A0-20: einmalige `at`-Festlegung pro Run) ändern — bleibt unverändert bindend.
|
||||
- Veränderung am §5.9-No-Op-Regeltext selbst (frozen, Story 3.1) — §5.16 ergänzt nur die operationelle CONFIRMING-Abgrenzung, kein Rework des frozen Textes.
|
||||
|
||||
**Never:**
|
||||
- Nie Wissensmutation bei Orphan/Hold; nie stilles Löschen von Evidenz, Provenienz oder geschützten Bestandteilen; nie „Regenerate Everything" (AD-5/FR-12); nie hart kodierte erwartete Pläne/Concept-Bodies/Zählwerte als Beweis (AC-7, §5.14 Pkt. 2); nie Standalone/Eigene Runtime (D-3, AD-11); nie textueller Auto-Merge (AD-17c).
|
||||
|
||||
## I/O & Edge-Case Matrix
|
||||
|
||||
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|
||||
|----------|--------------|---------------------------|----------------|
|
||||
| KONTINUITAET | neue Erkenntnis auf bestehenden Concept-Pfad | in-place Update/Synthese, kein Duplikat, Identität + Index-Link erhalten (AC-1) | kein Overwrite, kein neuer Link ohne echte Beziehung |
|
||||
| KORRIGIEREN_RECEIPT | eindeutige aktuelle Evidenz ersetzt eine Aussage | in-place Korrektur; ersetzte Wortlautfolge + Source-Basis im Run-Receipt (§5.14 Pkt. 2) (AC-2) | mehrdeutig → benannter Hold, keine Mutation |
|
||||
| GESCHUETZTE_BESTANDTEILE | Update/Synthese auf Concept mit gültigen Aussagen+Provenienz | geschützte Bestandteile erhalten; nicht betroffene Concepts byte-identisch (AC-3) | Verstoß = textuell benannt (NFR-4), Ghost-Diff-Rollback (§5.9 Pkt. 5) |
|
||||
| CONFIRMING | neue Source bestätigt bestehende Aussage unabhängig | Aussage genau einmal, alle beitragenden Evidenzanker (§5.5-Multi-Beleg), `sources`-Zuwachs, `at`-Bump — **kein NO_OP** (AC-4) | kein Duplikat der Aussage, kein stiller Anker-Verlust |
|
||||
| SYNTHESE_ERHALTUNG | mehrere redundante/ergänzende Sources | eine gemeinsame Wissensrepräsentation, claim-granulare gemischte Provenienz, keine Quelle-A/B-Aneinanderreihung (AC-5) | §5.10-Pkt.-5-Reflektiertheits-Selbsttest |
|
||||
| NO_OP_BYTE_ERHALTEND | identische Evidenz samt vollständiger Evidenzanker im Body | byte-erhaltender NO_OP — keine Mutation, kein `at`-Bump, kein `sources`-Zusatz, kein `log.md`-Eintrag (AC-6) | fehlender Anker → CONFIRMING (Pkt. 4), nicht NO_OP |
|
||||
| SELBSTTEST_AKTUELLER_RUN | Provenienz-/Link-Selbsttest bei Update/Synthese | Baseline = aktuelles `<Baseline-Commit>`; erwartete Deltas = Kandidatenliste ∪ Neu-Anlage ∪ `log.md` ∪ `index.md` (AC-7) | kein historischer Commit/Zählwert normativ; Abweichung = textuell benannt |
|
||||
| ORPHAN_HOLD_VOLLE_STUFEN | unzugeordnete Evidenz, erkannt über Stufe a/b/c | benannter Hold ohne Wissensmutation, datumsgruppierter `log.md`-Eintrag (`<Baseline-Commit>`), beide Evidenzpfade im Run-Receipt (AC-8) | kein Banner, keine stille Bearbeitung, keine eigenständige Anlage |
|
||||
| MEHRZIEL | eine Einheit löst über mehrere Terme auf mehrere Ziele auf | Konsolidierung auf primäre Ziel-Repräsentation; kein Duplikat; ohne dominantes Ziel → benannter Hold (AC-8) | mehrdeutig → fail-closed (Hold), beide Pfade im Receipt |
|
||||
|
||||
## Code Map
|
||||
|
||||
- `schema/compiler.md` — **primär mutiert** (D-3): neue Sektion **§5.16 „Inkrementelle Update- & Synthese-Erhaltung + Hold-Ausbau (Story 3.10)"** (nach §5.15 Z. 398, vor §6 Z. 402; Pkt. 1–8) — alle Zellen referenzieren die bestehenden Mechaniken Wortlaut-unverändert; **Hold-Home-Nachführungen**: §5.15-Zelle-3-Zeile (Z. 391 „Hold-Ausbau Home Story 3.10") und §5.15-Scope-Präzisierung (Z. 400 „Der Hold-Ausbau (post-Reconcile-Orphan, Mehrziel) verbleibt Story 3.10") → „in §5.16 verankert (Story 3.10)"; §5.10-Pkt.-8-D-4-Bullet (Z. 302 „hat seinen Home in Story 3.10") → §5.16-Verankerung; §7-Bullet-Erweiterung (Story-3.10-Verankerung, analog §5.15-Ergänzung); §8-Revisionslog **Revision 3.5**. Bestehende §5.9/§5.10/§5.15-Mechaniken bleiben **textuell unverändert** (Referenz statt Re-Negotiation).
|
||||
- `_bmad-output/implementation-artifacts/sandbox-3-10/run-sandbox.sh` — **neu** (re-executierbar, Muster `sandbox-3-9/run-sandbox.sh`, Exit 0): Szenarien E-1..E-9 (Matrix-Zeilen als harte Assertionen; CONFIRMING ≠ NO_OP mit Byte-Vergleich; NO_OP byte-erhaltend; Korrigieren-Receipt-Trace; aktueller-Run-Selbsttest; Orphan über Stufe-a/b/c; Mehrziel; Zwei-Run-Identität).
|
||||
- `_bmad-output/implementation-artifacts/sprint-status.yaml` — **mutiert**: Key `3-10-inkrementelle-update-und-synthese-erhaltung-absichern` `backlog` → `review` (Implementierungs-Commit; finaler `done`-Flip im Step-05-Status-Sync nach konvergiertem Review-Loop); `last_updated` (Format `MM-DD-YYYY HH:MM`).
|
||||
- `_bmad-output/implementation-artifacts/deferred-work.md` — **append**: Defer „Orphan voller Stufen b/c + Mehrziel" (Hold-Home Story 3.10) → aufgegriffen/geschlossen; Umlaut-Defer bleibt offen (wie notiert, kein Instruktions-Defekt).
|
||||
- `wiki/log.md` — **append** (Vertrag §5, bestehende Bullets unverändert): Story-3.10-Eintrag (Verankerung §5.16, Sandbox-Nachweis E-1..E-9, Status-Flip, Validator-Verdikt).
|
||||
- `_bmad-output/implementation-artifacts/epic-3-context.md` — **mutiert** (gemäß genehmigtem Sprint-Change-Proposal 2026-08-20; Header „Edit freely"): Technical Decision Z. 39 „Deterministische Relevanz & Routing" um §5.16-/Story-3.10-Ist ergänzt (Erhaltungs-Absicherung + Hold-Ausbau verankert).
|
||||
|
||||
**Read-only evidence (AD-3):** `schema/validator.md` (Rev 9), `schema/wiki-compiler.md`, `adapters/`, `raw/` (z. B. `raw/architecture-spine/architecture-spine-2026-08-14.md` AD-4/AD-5/AD-16; `raw/epics/epics-2026-08-14.md` FR-4/FR-6/FR-7/FR-12, NFR-7). `schema/canonical-terms.md` append-only unangetastet.
|
||||
|
||||
## Tasks & Acceptance
|
||||
|
||||
**Execution:**
|
||||
- [x] `schema/compiler.md` — §5.16 einfügen (nach §5.15, vor §6): Pkt. 1–8 gemäß Intent; keine neuen Prädikate/§7-Klassen/Keys; Hartung der CONFIRMING-vs-NO_OP-Grenze; §7-Bullet und §8-Revisionslog **Revision 3.5** nachführen; Hold-Home-Nachführungen §5.15-Zelle-3/Scope-Präzisierung, §5.10-Pkt.-8-D-4
|
||||
- [x] `_bmad-output/implementation-artifacts/sandbox-3-10/run-sandbox.sh` — E-1..E-9, harte PASS/FAIL, CONFIRMING ≠ NO_OP byte-bewiesen, Zwei-Run nicht-vakuum, Exit 0
|
||||
- [x] `_bmad-output/implementation-artifacts/sprint-status.yaml` — Key 3-10 → `review` (Impl-Commit; finaler `done`-Flip Step-05); `deferred-work.md` — Orphan-voller-Stufen/Mehrziel-Defer aufgegriffen; `wiki/log.md` — Story-3.10-Eintrag (Verankerung, Sandbox, Validator-Verdikt)
|
||||
- [x] `_bmad-output/implementation-artifacts/epic-3-context.md` — Technical Decision Z. 39 um §5.16-Ist ergänzt
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Given eine passende neue Erkenntnis zu einem bestehenden Concept, when der Run sie verarbeitet, then wird das bestehende Concept in-place erweitert oder präzisiert und kein thematisches Duplikat angelegt (AC-1).
|
||||
- Given explizite aktuelle Evidenz, die eine bestehende Aussage eindeutig korrigiert, ohne dass zwischen weiterhin gültigen Sources ein Widerspruch verbleibt, when der Run sie verarbeitet, then wird das bestehende Concept in-place korrigiert und die ersetzte Aussage samt Source-Basis bleibt im Run-Receipt nachvollziehbar; mehrdeutige Fälle gehen in den Hold für Epic 4 (AC-2).
|
||||
- Given ein betroffenes Concept mit weiterhin gültigen Aussagen und Provenienz, when es aktualisiert wird, then bleiben diese geschützten Bestandteile erhalten; nicht betroffene Concepts bleiben byte-identisch (AC-3).
|
||||
- Given eine neue Source, die eine bestehende Aussage unabhängig bestätigt, when synthetisiert wird, then erscheint die Aussage genau einmal und trägt alle beitragenden Evidenzanker; die neue Source ist kein `NO_OP` (AC-4).
|
||||
- Given mehrere Sources mit redundanten und ergänzenden Aussagen, when der Run synthetisiert, then entsteht eine gemeinsame Wissensrepräsentation mit claim-granularer gemischter Provenienz — keine Source-A/Source-B-Aneinanderreihung (AC-5).
|
||||
- Given eine bereits vollständig repräsentierte identische Source samt Evidenzanker, when sie erneut verarbeitet wird, then ist der Run für dieses Wissen byte-erhaltend (`NO_OP`) (AC-6).
|
||||
- Given der Provenienz- und Link-Selbsttest, when erwartete Deltas bestimmt werden, then stammen Baseline und Erwartung aus dem aktuellen Run; kein historischer, fest codierter Commit oder globaler Zählwert ist normativ (AC-7).
|
||||
- Given klassifikationspflichtige oder widersprüchliche Evidenz, when der Run sie erreicht, then wird sie bis Epic 4 ohne Wissensmutation in einem benannten Hold erhalten; beide Evidenzpfade bleiben im Run-Receipt nachvollziehbar (NFR-7) — der post-Reconcile-Orphan wird über alle Erhebungs-Stufen (a/b/c) geschlossen und Mehrziel-Einheiten werden konsolidiert oder gehalten (AC-8).
|
||||
|
||||
## Spec Change Log
|
||||
|
||||
- **Draft (2026-08-21, Step-02-Plan):** Erstentwurf gemäß bmad-build-Workflow für Story 3.10 — Erhaltungs-Absicherung (AC-1..AC-7) + Hold-Ausbau (AC-8, Hold-Home aus §5.15-Zelle-3/Scope-Präzisierung und §5.10-Pkt.-8-D-4). Noch keine Review-Loop-Einträge; `status: draft`, Review-Loop-Iteration 0.
|
||||
|
||||
</frozen-after-approval>
|
||||
|
||||
## Design Notes
|
||||
|
||||
**Warum §5.16 als eigene Sektion, nicht §5.9/§5.10-Umbau?** §5.9 (frozen, Story 3.1/3.3) und §5.10 (Story 3.4) sind textuell verankerte Mechaniken; §5.15 (Story 3.9) schließt die Routing-Ebene. §5.16 ist die **Erhaltungs-Klammer** darüber — dieselbe Fugen-Identitäts-Präzedenz wie §5.14/§5.15: die operationelle Ebene und der Hold-Ausbau werden als neue Sektion verankert, bestehende Regeltexte Wortlaut-unverändert referenziert (Re-Negotiation-Vermeidung).
|
||||
|
||||
**CONFIRMING ≠ NO_OP (AC-4/AC-6-Grenze ist der Kernfnd der Erhaltung).** Der §5.9-No-Op („engere Auslegung: trifft nur, wenn die neue Evidenz keine Aussage trägt, die im Body nicht bereits als belegte Aussage vorhanden ist — Term-/Stellen-Abgleich mit §5.5-Inline-Verweisen") muss operationell gegen den zunächst verblüffend ähnlichen CONFIRMING-Fall abgegrenzt werden: Eine bestätigende Source trägt **denselben Inhalt, aber einen neuen Evidenzanker** (anderer `raw/`-Pfad/-Stelle). Der Stellen-Abgleich schlägt also auf den **Anker**, nicht auf den Inhalt: ein fehlender Anker = echter neuer Beleg → Konsolidierungs-Update (Pkt. 4), kein NO_OP. NO_OP bleibt AC-6 vorbehalten: **vollständige** Evidenzanker-Menge bereits im Body präsent. Diese Abgrenzung ist rein operationell (neue §5.16-Ebene) — der frozen §5.9-Regeltext bleibt unverändert.
|
||||
|
||||
**Run-Receipt trägt die Korrektur-/Hold-Spur (AC-2/AC-8).** Der §5.14-Pkt.-2-Run-Receipt liegt außerhalb des Bundles und ist damit der korrekte Ort für ersetzte Wortlautfolgen (Korrigieren) und die beiden Evidenzpfade eines Holds (ursprünglicher + widersprechender) — das Bundle selbst enthält laut AD-16-Default beide Behauptungen, ohne Korrektur-Klassifikation (Epic 4). NFR-7 („ohne künstliche Gewissheit") wird damit mechanisch nachvollziehbar: Der Hold ist ein **benannter**, im `log.md` datumsgruppierter Eintrag + Receipt-Führung, nie eine stille Löschung oder eigenständige Concept-Anlage.
|
||||
|
||||
**Aktueller-Run-Selbsttest (AC-7) löst die historischen Fixwerte auf.** Die §5.6-Formeln 1–4 tragen historische Zählwerte/Baselines als dokumentarische Pins (Story 2.3/2.4). AC-7 macht für die Erhaltungs-Probe die **Relativ-Ableitung** normativ: Baseline = `<Baseline-Commit>` des aktuellen Runs (dynamische Extraktion), erwartete Delta-Menge = Kandidatenliste ∪ Neu-Anlage ∪ `log.md` ∪ nachgeführte `index.md` — kein globaler Zählwert liefert noch eine normative Erwartung mit.
|
||||
|
||||
**Hold-Ausbau = post-Reconcile-Orphan über volle Stufen + Mehrziel.** §5.15 Zelle 3 ist der Routing-Anker (fail-closed, `raw/` unangetastet); §5.10 Pkt. 8 ist die deterministische Reconcile-Orphan-Regel (log.md-verwaist-Eintrag). §5.16 Pkt. 8 schließt die von §5.15/§5.14 offen gelassenen Fälle: (a) Orphan-Erkennung über **alle drei** Erhebungs-Stufen (§3.2 a/b/c — die Story-3.9-Sandbox übte nur Stufe-a), (b) **Mehrziel** aus der D-8-Mehrfach-Term-Vereinigung (Eine Einheit → mehrere gleichgewichtige Ziele: Konsolidierung auf eine primäre Ziel-Repräsentation, nie Duplikat-Inhalt in mehreren Concepts; ohne dominantes Ziel → fail-closed Hold), (c) benannter Hold mit beiden Evidenzpfaden im Receipt.
|
||||
|
||||
## Verification
|
||||
|
||||
**Commands (re-executierbar, ab Workspace-Root):**
|
||||
1. `bash _bmad-output/implementation-artifacts/sandbox-3-10/run-sandbox.sh` — expected: E-1..E-9 harte PASS/FAIL, CONFIRMING ≠ NO_OP byte-bewiesen (Aussage genau einmal, neuer Anker zusätzlich, kein `NO_OP`-Pfad), NO_OP byte-erhaltend (vollständige Anker-Menge), Korrigieren-Receipt-Trace vorhanden, aktueller-Run-Selbsttest ohne hart kodierte Erwartung, Orphan über Stufe-a/b/c + Mehrziel-Hold, Zwei-Run-Identität nicht-vakuum, Exit 0.
|
||||
2. `grep -n "§5.16\|Revision 3.5" schema/compiler.md` — §5.16-Sektion + Revisionslog-Eintrag; `grep -n "Inkrementelle Update- & Synthese-Erhaltung" schema/compiler.md` — Überschrift wortgleich; Hold-Home-Nachführung: `grep -n "in §5.16 verankert" schema/compiler.md`.
|
||||
3. Read-only (AD-3): `git status --porcelain` zeigt keinen Change an `schema/validator.md`/`schema/wiki-compiler.md`/`adapters/`/`raw/`; `schema/canonical-terms.md` unverändert.
|
||||
4. Validator-Lauf: alle `wiki/`-Dateien SUCCESS (keine Inhalts-Mutation); auf `wiki/` begrenzt (`git status --porcelain -- wiki/`): ausschließlich `wiki/log.md` — Erhaltungs-Invariante §5.9 Pkt. 5 gewahrt.
|
||||
|
||||
**Zu beachten (beim step-04-Review):** (a) bestehende §5.9/§5.10/§5.15-Mechaniken **textuell unverändert** (Referenz statt Re-Negotiation — §5.16 verankert nur die Erhaltungs-/Hold-Ebene); (b) die vorhandene Revisionslog-Nummer ist **3.4** (Story 3.9) — **Revision 3.5 ist für Story 3.10 frei**; (c) Umlaut-vs-Transkription-Defer bleibt offen (kein stiller Abschluss); (d) §5.16 führt **kein** neues Prädikat/keine neue §7-Klasse/keinen neuen Key ein; (e) AD-16-Klassifikation bleibt Epic 4.
|
||||
|
||||
## Suggested Review Order
|
||||
|
||||
**Design-Intent & Einstieg**
|
||||
|
||||
- Erhaltungs-Klammer als eigene Sektion (Story 3.10) — operationelle Ebene über textuell unveränderten §5.9/§5.10/§5.15-Mechaniken, kein Umbau.
|
||||
[`compiler.md:402`](../../schema/compiler.md#L402)
|
||||
|
||||
**Instruktions-Verankerung (§5.16)**
|
||||
|
||||
- CONFIRMING ≠ NO_OP: neue Source mit neuem Evidenzanker = Konsolidierungs-Update, nie No-Op (AC-4).
|
||||
[`compiler.md:402`](../../schema/compiler.md#L402)
|
||||
- NO_OP byte-erhaltend nur bei vollständiger Anker-Menge (AC-6); Korrigieren mit Run-Receipt-Trace (AC-2); Schutzbestandteile & Byte-Identität (AC-3).
|
||||
[`compiler.md:402`](../../schema/compiler.md#L402)
|
||||
- Benannter Hold über Erhebungs-Stufen a/b/c + Mehrziel-D-8-Vereinigung, beide Evidenzpfade im Receipt (AC-8).
|
||||
[`compiler.md:402`](../../schema/compiler.md#L402)
|
||||
- Selbsttest aus aktuellem Run: Baseline = `<Baseline-Commit>`, erwartete Deltas, keine historischen Zählwerte (AC-7).
|
||||
[`compiler.md:402`](../../schema/compiler.md#L402)
|
||||
|
||||
**Anker-Synchronisierung**
|
||||
|
||||
- Hold-Home-Nachführungen: §5.15 Zelle 3 + Scope-Präzisierung, §5.10 Pkt. 8 D-4-Bullet — „in §5.16 verankert, Story 3.10".
|
||||
[`compiler.md:391`](../../schema/compiler.md#L391)
|
||||
- §7-Bullet Story-3.10-Verankerung; §8-Revisionslog **Revision 3.5** (2026-08-21, Story 3.10).
|
||||
[`compiler.md:515`](../../schema/compiler.md#L515)
|
||||
|
||||
**Verifikations-Nachweis (Test)**
|
||||
|
||||
- Sandbox E-1..E-9 — re-executierbarer Erhaltungs-Nachweis; Stufen-Scans a/b/c + route_orphan/mehrziel_route (echte Mechanik, kein Fixture-Marker-Wiedergepen).
|
||||
[`run-sandbox.sh:127`](sandbox-3-10/run-sandbox.sh#L127)
|
||||
- E-4 CONFIRMING ≠ NO_OP byte-bewiesen (at-Bump aus aktuellem Run, A0-20); E-6 NO_OP byte-erhaltend auf Run-State-Ebene (git-Status leer).
|
||||
[`run-sandbox.sh:356`](sandbox-3-10/run-sandbox.sh#L356)
|
||||
- E-8 Orphan/Mehrziel-Hold (log.md statt Sidecar, nur-log.md-Delta) + E-9 at-Wanduhr-Gap-Exzeption real ausgeübt.
|
||||
[`run-sandbox.sh:512`](sandbox-3-10/run-sandbox.sh#L512)
|
||||
|
||||
**Peripherie (Status/Tracking)**
|
||||
|
||||
- sprint-status.yaml Key 3-10 → review (finaler done-Flip nach Step-05-Status-Sync); deferred-work.md Aufgegriffen-/Defer-Einträge; wiki/log.md Story-3.10-Eintrag; epic-3-context Technical Decision Z. 39.
|
||||
[`sprint-status.yaml:47`](../../_bmad-output/implementation-artifacts/sprint-status.yaml#L47)
|
||||
@@ -29,7 +29,7 @@
|
||||
# - Dev moves story to 'review', then runs code-review (fresh context, different LLM recommended)
|
||||
# - Retrospective appends its action items to action_items; the status view surfaces open ones
|
||||
generated: 08-14-2026 00:00
|
||||
last_updated: 08-21-2026 10:16
|
||||
last_updated: 08-21-2026 12:00
|
||||
project: wow20
|
||||
project_key: NOKEY
|
||||
tracking_system: file-system
|
||||
@@ -60,7 +60,7 @@ development_status:
|
||||
3-7-reason-mutate-trennung-und-konsistenz-endzustand-sicherstell: done
|
||||
3-8-determinismus-vertrag-ad-17h-als-agent-instruktions-validato: done # Review-Loop-3-Abschluss 2026-08-20 (bmad-code-review; D-2-Entscheidung). Hinweis: die Abschluss-Reihenfolge „3.9 → 3.10 / 3.11 → 3.12 → 3.8 Abschluss → 3.13 Abnahme" des genehmigten Sprint-Change-Proposal 2026-08-20 betrifft das Epic-3-Abnahmegate (3.13) — die Story-3.8-Instruktionsverankerung ist abgeschlossen (done); die 3.9–3.12-Verankerungen und die 3.13-Abnahme bleiben offen (epic-3 remains in-progress).
|
||||
3-9-deterministische-relevanz-und-reconcile-routing-schliessen: done # Review-Loop-3-Abschluss 2026-08-21 (bmad-code-review, 4 Layer; D-3.9-1/2/3 = 1/1/2 — empfohlene Optionen; kein Loopback). Hinweis: der finale `done`-Flip ist der Step-05-Status-Sync nach konvergiertem Review-Loop (Präzedenz Story 3.7/3.8); die Epic-3-Abnahme (3.13) bleibt offen (epic-3 remains in-progress).
|
||||
3-10-inkrementelle-update-und-synthese-erhaltung-absichern: backlog
|
||||
3-10-inkrementelle-update-und-synthese-erhaltung-absichern: done # Story 3.10 Abschluss 2026-08-21 (bmad-code-review, 3 Layer; Patch-Kaskade, keine intent_gap/bad_spec — Sandbox E-1..E-9 nach Härtung 9/9 harte PASS/Exit 0, §5.16 Rev 3.5). Hinweis: die Epic-3-Abnahme (3.13) bleibt offen (epic-3 remains in-progress).
|
||||
3-11-root-scope-leasing-atomar-akquirieren: backlog
|
||||
3-12-lease-lifecycle-und-commit-abschluss-transaktional-schliessen: backlog
|
||||
3-13-epic-3-verifikations-und-abnahmegate: backlog
|
||||
|
||||
+18
-4
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user