Three minor releases at once. Byte-identical surface: only WORKFLOW.md actually changed (+128/-2); CLAUDE.md and the four templates are untouched. AGENTS.md takes the new upstream prefix (+22/-2) and keeps our project section unchanged. The two declared-extended files were reconciled by hand, because nothing compares them: validate.py gains upstream's check_vendored_portable, and schema.yaml gains vendored, project_section_marker, the judged link field and the ledger and verdict types. gen_status.py needed nothing — upstream did not touch it. Recorded in the new HERKUNFT.md, including that this reconciliation has no contradictor and will be forgotten next time. Newly adopted: check_harvest.py and judge.py, plus docs/ledger/ and docs/verdict/ with their templates. Deliberately not adopted: check_locked.py — pruefe_sperrliste.py has done that job here since 2026-08-20, and two tools for one rule is maintenance without gain. The v0.1.1 baseline stays where it is; docs/sources is immutable.
82 lines
3.0 KiB
Python
82 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
||
"""pruefe_upstream_drift.py — Byte-Vergleich gegen die gepinnte Baseline.
|
||
|
||
Schützt die übernommenen Framework-Dateien vor stillem Umschreiben
|
||
(Entscheidung 8 im Design 2026-08-11, Frage von sorb: „wird die
|
||
AGENTS.md ggf. durch Agenten umgeschrieben?"). Die Baseline liegt unter
|
||
docs/sources/upstream/neckbeard-v0.3.1/ (siehe HERKUNFT.md dort); ein
|
||
Framework-Upgrade aktualisiert Baseline und Arbeitskopie im selben,
|
||
bewussten Commit.
|
||
|
||
Prüfungen (Fehler, Exit 1):
|
||
* byte-identische Paare laut PAARE
|
||
* AGENTS.md beginnt byte-identisch mit der Baseline-AGENTS.md und
|
||
trägt direkt danach die Marke des Projektabschnitts
|
||
Fehlt eine Baseline-Datei, ist das ein Fehler, kein Skip
|
||
(Stillstandsprüfungs-Regel: eine Prüfung ohne Gegenseite ist ungeprüft).
|
||
|
||
Usage: python scripts/pruefe_upstream_drift.py [repo-root]
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
BASELINE = "docs/sources/upstream/neckbeard-v0.3.1"
|
||
MARKE = "<!-- projektabschnitt -->"
|
||
|
||
# (Arbeitskopie, Baseline-Datei) — byte-identisch
|
||
PAARE = [
|
||
("CLAUDE.md", "CLAUDE.md"),
|
||
("WORKFLOW.md", "WORKFLOW.md"),
|
||
("docs/adr/template.md", "templates/adr-template.md"),
|
||
("docs/design/template.md", "templates/design-template.md"),
|
||
("docs/aar/template.md", "templates/aar-template.md"),
|
||
("docs/issues/template.md", "templates/issue-template.md"),
|
||
("docs/ledger/template.md", "templates/ledger-template.md"),
|
||
("docs/verdict/template.md", "templates/verdict-template.md"),
|
||
]
|
||
|
||
|
||
def main() -> int:
|
||
root = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.cwd()
|
||
base = root / BASELINE
|
||
fehler: list[str] = []
|
||
|
||
for arbeit, original in PAARE:
|
||
a, b = root / arbeit, base / original
|
||
if not b.is_file():
|
||
fehler.append(f"Baseline fehlt: {BASELINE}/{original}")
|
||
continue
|
||
if not a.is_file():
|
||
fehler.append(f"Arbeitskopie fehlt: {arbeit}")
|
||
continue
|
||
if a.read_bytes() != b.read_bytes():
|
||
fehler.append(f"DRIFT: {arbeit} weicht von {BASELINE}/{original} ab")
|
||
|
||
agents, agents_base = root / "AGENTS.md", base / "AGENTS.md"
|
||
if not agents_base.is_file():
|
||
fehler.append(f"Baseline fehlt: {BASELINE}/AGENTS.md")
|
||
elif not agents.is_file():
|
||
fehler.append("Arbeitskopie fehlt: AGENTS.md")
|
||
else:
|
||
upstream = agents_base.read_bytes()
|
||
arbeit = agents.read_bytes()
|
||
if not arbeit.startswith(upstream):
|
||
fehler.append("DRIFT: AGENTS.md — Upstream-Teil (§1–5) ist "
|
||
"nicht mehr byte-identisch mit der Baseline")
|
||
else:
|
||
rest = arbeit[len(upstream):].decode("utf-8", "replace")
|
||
if MARKE not in rest.splitlines()[0:3]:
|
||
fehler.append(f"AGENTS.md: Marke '{MARKE}' fehlt direkt "
|
||
"nach dem Upstream-Teil")
|
||
|
||
for f in fehler:
|
||
print(f"FEHLER {f}")
|
||
print(f"pruefe_upstream_drift: {len(fehler)} Fehler")
|
||
return 1 if fehler else 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|