feat(issues): adopt the component trackers — one backlog, one numbering (ADR-0019)

ADR-0012 made docs/issues/ canonical for the management scope only and left
gitops, ThreadNet-Web and threadnet-call on GitLab "until the component adopts".
That split produced exactly what it invited: two numbering worlds where
management#20 and gitops#20 are different issues, drift nobody had to answer for
(gitops#61 carried no milestone since 2026-08-11), and component backlogs that
host sessions without lab access cannot read at all.

The 46 open component issues are now files 0056-0101. The file id is the
group-wide identifier; provenance lives in the frontmatter (new field `projekt`
plus gitlab_iid) and in the filename, so "gitops#61" still finds 0091. Bodies are
copied verbatim; comments and history stay on GitLab, as with the 2026-08-11
management import.

Both scripts learned the second dimension: spiegel_issues.py routes each file to
its origin project, reopens issues that are open in the repo but closed on the
board, and writes the new iid back after creating one; gruppenpruefung.py checks
drift across all four trackers instead of management alone. What the mirror
cannot decide stays a finding, not a silent state.

Two things needed a hand, both recorded in the files: gitops#61 had no milestone
(M1 - it is a live account-takeover path) and carried two area labels where the
schema holds one. The Gitea migration footers in the imported bodies point at
decommissioned trackers; their links are removed, the provenance sentence stays.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Thore Cimbal
2026-08-18 12:00:00 +00:00
co-authored by Claude Opus 5
parent 5ec4aa702e
commit a1def8666e
52 changed files with 1789 additions and 44 deletions
+24 -12
View File
@@ -14,8 +14,10 @@ zu überspringen, und jede Prüfung bildet einen real passierten Fall ab
3. Meilenstein- und Prioritätspflicht über ALLE offenen
Gruppen-Issues. (realer Fall: gitops#61, entstanden 2026-08-11,
ohne Meilenstein)
4. Issue-Drift management ↔ docs/issues/ — Titel, Zustand,
Meilenstein, Priorität, Status-Label. (F-001/F-017-Klasse)
4. Issue-Drift GitLab ↔ docs/issues/ — Titel, Zustand, Meilenstein,
Priorität, Status-Label; seit ADR-0019 über management UND die
adoptierten Komponenten-Tracker (Feld `projekt`).
(F-001/F-017-Klasse)
5. Git-Hygiene seit 2026-08-07 — Autor- und Committer-Zeit 12:00:00
UTC, kanonische Identität; ausgenommen sind maschinelle Absender
(MASCHINEN, per Adresse — ADR-0009). (F-002/F-003)
@@ -188,29 +190,39 @@ def main() -> int:
if len(prios) != 1:
befunde.append(f"{ref}: {len(prios)} priority-Labels statt 1")
# 4) Issue-Drift management ↔ docs/issues/
gl = {i["iid"]: i for i in
api(f"projects/{urllib.parse.quote(GRUPPE + '/management', safe='')}"
f"/issues?state=opened", token)}
# 4) Issue-Drift GitLab ↔ docs/issues/ (management + adoptierte
# Komponenten-Tracker, ADR-0019)
spiegel_projekte = {
"management": "axion1337.chat/management",
"gitops": "axion1337.chat/axion1337.chat-gitops",
"threadnet-web": "axion1337.chat/ThreadNet-Web",
"threadnet-call": "axion1337.chat/threadnet-call",
}
gl = {}
for kurz, pfad in spiegel_projekte.items():
for i in api(f"projects/{urllib.parse.quote(pfad, safe='')}"
f"/issues?state=opened", token):
gl[(kurz, i["iid"])] = i
dateien = {}
for f in sorted((root / "docs/issues").glob("[0-9]*.md")):
meta = frontmatter(f)
proj = meta.get("projekt") or "management"
if meta.get("gitlab_iid"):
dateien[int(meta["gitlab_iid"])] = (f, meta)
dateien[(proj, int(meta["gitlab_iid"]))] = (f, meta)
elif meta.get("status") not in ("done", "rejected"):
hinweise.append(f"{f.name}: noch nicht gespiegelt "
f"(gitlab_iid fehlt — erwartet bis zum "
f"ersten Spiegel-Lauf)")
for iid in sorted(set(gl) - set(dateien)):
befunde.append(f"management#{iid} ist offen auf GitLab, hat aber "
for proj, iid in sorted(set(gl) - set(dateien)):
befunde.append(f"{proj}#{iid} ist offen auf GitLab, hat aber "
f"keine kanonische Datei (zweites Backlog!)")
for iid, (f, meta) in sorted(dateien.items()):
if iid not in gl:
for (proj, iid), (f, meta) in sorted(dateien.items()):
if (proj, iid) not in gl:
if meta.get("status") not in ("done", "rejected"):
befunde.append(f"{f.name}: offen im Repo, aber auf GitLab "
f"geschlossen/fehlend — nachziehen")
continue
i = gl[iid]
i = gl[(proj, iid)]
if titel(f) != i["title"].strip():
befunde.append(f"{f.name}: Titel weicht von GitLab ab")
ms = (i.get("milestone") or {}).get("title", "")