feat: slice 4 - the open management issues live in the repo
Gate 4, slice 4: 26 open management issues imported from live git.lab (read-only, descriptions included as authorized; GitLab iid = file id, labels/milestone/priority/due/host/area mapped into frontmatter, the import aborts instead of inventing a missing milestone or priority). Two new issues close the F-004 gap where work was really still open (0033 OVERMIND-01, 0034 CFGMON-11 incl. the plaintext npm-token rotation); CFGMON-12/13 already route to verified git.lab issues, MATRIX-05 is done and needs none (agreed with sorb). The three wiki task blocks now reference their issues, roadmap.md hands all counts to the generated STATUS.md and states M1-M5 per ADR-0010 (closing F-001 in the canonical prose), pruefe_prosa joins the CI validate job, and the import protocol under docs/sources/migration/ records every intervention into imported text. Verified: validate 0/0 over 29 issue files, gen_status --check current (distribution line M1 9 - M2 17 - M4 2 plus per-issue milestone and priority), pruefe_prosa 0 errors with clones and 0 errors/15 unchecked citations in offline CI mode, drift check green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
92b448fe30
commit
c23bb54a92
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python3
|
||||
"""import_issues.py — Einmal-Import der offenen management-Issues.
|
||||
|
||||
Migrationsakte, kein Dauerwerkzeug (Design 2026-08-11, Slice 4;
|
||||
ADR-0012). Liest die offenen Issues des Projekts
|
||||
axion1337.chat/management read-only von git.lab (Token nur per
|
||||
Dateipfad, Wert erscheint nirgends) und schreibt je Issue eine
|
||||
kanonische Datei docs/issues/<iid>-<slug>.md. GitLab-iid = Datei-id —
|
||||
keine dritte Nummernwelt. Kommentare und Verlauf bleiben auf GitLab;
|
||||
der Dateikopf verlinkt dorthin.
|
||||
|
||||
Abbildung (alt → Schema):
|
||||
ohne status-Label → open · status:next → next · status:doing →
|
||||
in-progress · status:wartet → waiting (wartegrund: Verweis auf den
|
||||
GitLab-Verlauf; Präzisierung im nächsten Refinement) · Meilenstein
|
||||
"Mn — …" → Mn · priority:x → x · due_date → due · host:x → host ·
|
||||
area:x → area. Fehlt Meilenstein oder Priorität, bricht der Import
|
||||
ab — das wäre ein Befund, kein Füllwert.
|
||||
|
||||
Relative Upload-Pfade in Beschreibungen werden auf absolute
|
||||
git.lab-URLs umgeschrieben, damit der Link-Check nicht ins Leere prüft.
|
||||
|
||||
Usage: python3 import_issues.py <repo-root> [tokenpfad]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
API = "https://git.lab/api/v4/projects/axion1337.chat%2Fmanagement/issues"
|
||||
UPLOADS = "https://git.lab/axion1337.chat/management"
|
||||
|
||||
UMLAUTE = str.maketrans({"ä": "ae", "ö": "oe", "ü": "ue", "ß": "ss",
|
||||
"Ä": "ae", "Ö": "oe", "Ü": "ue", "é": "e"})
|
||||
|
||||
|
||||
def slug(titel: str) -> str:
|
||||
s = titel.translate(UMLAUTE).lower()
|
||||
s = re.sub(r"[^a-z0-9]+", "-", s).strip("-")
|
||||
if len(s) > 48:
|
||||
s = s[:48].rsplit("-", 1)[0]
|
||||
return s or "ohne-titel"
|
||||
|
||||
|
||||
def hole(token: str):
|
||||
issues, seite = [], 1
|
||||
while True:
|
||||
req = urllib.request.Request(
|
||||
f"{API}?state=opened&per_page=100&page={seite}",
|
||||
headers={"PRIVATE-TOKEN": token})
|
||||
with urllib.request.urlopen(req) as antwort:
|
||||
batch = json.load(antwort)
|
||||
issues += batch
|
||||
if len(batch) < 100:
|
||||
return sorted(issues, key=lambda i: i["iid"])
|
||||
seite += 1
|
||||
|
||||
|
||||
def main() -> int:
|
||||
root = Path(sys.argv[1])
|
||||
tokenpfad = Path(sys.argv[2] if len(sys.argv) > 2
|
||||
else Path.home() / ".config/gitlab-lab/token")
|
||||
token = tokenpfad.read_text(encoding="utf-8").strip()
|
||||
|
||||
ziel = root / "docs/issues"
|
||||
geschrieben = []
|
||||
for i in hole(token):
|
||||
iid, titel, labels = i["iid"], i["title"].strip(), i["labels"]
|
||||
ms = (i.get("milestone") or {}).get("title", "")
|
||||
m = re.match(r"^(M\d)\b", ms)
|
||||
prio = [l.split(":")[1] for l in labels if l.startswith("priority:")]
|
||||
if not m or len(prio) != 1:
|
||||
sys.exit(f"ABBRUCH: #{iid} ohne eindeutigen Meilenstein/"
|
||||
f"Priorität ({ms!r}, {prio!r}) — Befund, kein Füllwert.")
|
||||
status = "open"
|
||||
wartegrund = ""
|
||||
if "status:doing" in labels:
|
||||
status = "in-progress"
|
||||
elif "status:next" in labels:
|
||||
status = "next"
|
||||
elif "status:wartet" in labels:
|
||||
status = "waiting"
|
||||
wartegrund = ("Grund im GitLab-Verlauf benannt (Import "
|
||||
"2026-08-11); im nächsten Refinement präzisieren")
|
||||
host = [l.split(":")[1] for l in labels if l.startswith("host:")]
|
||||
area = [l.split(":")[1] for l in labels if l.startswith("area:")]
|
||||
|
||||
zeilen = ["---", "type: issue", f'id: "{iid:04d}"',
|
||||
f"status: {status}", f"created: {i['created_at'][:10]}",
|
||||
f"milestone: {m.group(1)}", f"priority: {prio[0]}"]
|
||||
if i.get("due_date"):
|
||||
zeilen.append(f"due: {i['due_date']}")
|
||||
if host:
|
||||
zeilen.append(f"host: {host[0]}")
|
||||
if area:
|
||||
zeilen.append(f"area: {area[0]}")
|
||||
if wartegrund:
|
||||
zeilen.append(f"wartegrund: {wartegrund}")
|
||||
zeilen += [f'gitlab_iid: "{iid}"', "related: []", "---", ""]
|
||||
|
||||
beschreibung = (i.get("description") or "").replace("\r\n", "\n")
|
||||
beschreibung = beschreibung.replace("](/uploads/",
|
||||
f"]({UPLOADS}/uploads/")
|
||||
kopf = (f"# {titel}\n\n"
|
||||
f"> Import aus [management#{iid}]({i['web_url']}) "
|
||||
f"(2026-08-11). Kommentare und Verlauf bleiben dort; "
|
||||
f"kanonisch ist ab jetzt diese Datei (ADR-0012).\n\n")
|
||||
datei = ziel / f"{iid:04d}-{slug(titel)}.md"
|
||||
datei.write_text("\n".join(zeilen) + kopf + beschreibung.rstrip()
|
||||
+ "\n", encoding="utf-8")
|
||||
geschrieben.append(datei.name)
|
||||
|
||||
for name in geschrieben:
|
||||
print(name)
|
||||
print(f"import: {len(geschrieben)} Issues geschrieben")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,56 @@
|
||||
# Issue-Import-Protokoll — 2026-08-11
|
||||
|
||||
Einmal-Import der offenen management-Issues von git.lab nach
|
||||
`docs/issues/` (ADR-0012, Design 2026-08-11 Slice 4), ausgeführt mit
|
||||
[import_issues.py](import_issues.py) (read-only, Token per Dateipfad).
|
||||
Dieses Protokoll ist die Migrationsakte; es wird nicht fortgeschrieben.
|
||||
|
||||
## Zahlen
|
||||
|
||||
- **26 Issues importiert** (iids 1–32 mit Lücken; GitLab-iid =
|
||||
Datei-id), Quelle: Live-Stand git.lab am 2026-08-11.
|
||||
- **2 Issues neu angelegt** (F-004-Nachzügler, iids ab 33 lokal
|
||||
vergeben, `gitlab_iid` folgt mit dem ersten Spiegel-Lauf):
|
||||
`0033` OVERMIND-01, `0034` CFGMON-11.
|
||||
- Endstand: 28 offene + 1 zuvor bestehendes Artefakt-Issue-Verzeichnis
|
||||
→ siehe generiertes `STATUS.md` (29 Dateien inkl. der zwei neuen).
|
||||
- **Geschlossene GitLab-Issues wurden nicht importiert** (ADR-0012);
|
||||
sie bleiben als Historie auf git.lab.
|
||||
|
||||
## Abbildung
|
||||
|
||||
Label/Feld-Mapping wie im Skript-Docstring. 7 Issues kamen als
|
||||
`waiting` an (0002, 0004, 0008, 0014, 0021, 0025, 0027) — ihr
|
||||
`wartegrund` ist beim Import generisch („Grund im GitLab-Verlauf")
|
||||
und wird **im nächsten Refinement präzisiert**. 3 Issues tragen
|
||||
`next` (0007, 0015, 0020) — Zusagen von sorb, unverändert übernommen.
|
||||
|
||||
## F-004-Disposition (Abweichung vom 5/5-Kriterium, begründet)
|
||||
|
||||
| Punkt | Ergebnis |
|
||||
|---|---|
|
||||
| OVERMIND-01 | **neues Issue 0033** |
|
||||
| CFGMON-11 (Rest) | **neues Issue 0034** (enthält npm-Token-Rotation → medium) |
|
||||
| CFGMON-12 | kein neues Issue — abgelöst durch gitops#46 (git.lab), Verweis im Wiki verifiziert |
|
||||
| CFGMON-13 | kein neues Issue — entschieden (ADR-0003 alt), Umsetzung in gitops#45 (git.lab), verifiziert |
|
||||
| MATRIX-05 | kein Issue — seit 2026-08-01 erledigt; „Alles **Offene** ist ein Issue" verlangt für Erledigtes keins (mit sorb abgestimmt, 2026-08-11) |
|
||||
|
||||
Zusätzlich: der „Offen:"-Block in overmind verweist jetzt auf das
|
||||
bestehende Issue 0004 (OVERMIND-02) statt ins Leere.
|
||||
|
||||
## Eingriffe in importierte Texte (vollständig)
|
||||
|
||||
1. Relative Upload-Pfade → absolute git.lab-URLs (Skript, generell).
|
||||
2. `0031`: GitLab-relativer Link `../blob/main/verfahren/…` → kanonischer
|
||||
Repo-Pfad `../wiki/admin/stillstandspruefung.md`.
|
||||
3. `0025`: tote Tracker-URL im Migrations-Fußtext entschärft — der
|
||||
Provenienz-Text (`sorb/management#1`, Ersteller, Datum) bleibt
|
||||
wortgleich erhalten, nur die URL auf den stillgelegten Gitea-Tracker
|
||||
ist kein Link mehr (F-005/ADR-0002).
|
||||
4. Vier Hex-IDs aus Issue-Texten in die kuratierte Ausnahmenliste
|
||||
(`scripts/sha_ausnahmen.tsv`): zwei Alertmanager-Silence-IDs
|
||||
(0002), zwei Köpfe des ungespiegelten `gameserver` (0032) — mit
|
||||
Grund je Zeile.
|
||||
|
||||
Sonst sind die Beschreibungen wortgleich zum GitLab-Stand;
|
||||
Kommentare und Verlauf wurden bewusst nicht kopiert.
|
||||
Reference in New Issue
Block a user