Compare commits
16
Commits
@@ -33,3 +33,39 @@ stillstandspruefung:
|
||||
# Befunde sind kein Betriebsausfall, aber sie sollen sichtbar bleiben. Die rote
|
||||
# Pipeline ist bei uns die Alarmanlage (gitops/CLAUDE.md, TURN-Rotation).
|
||||
allow_failure: false
|
||||
|
||||
# Offline-Gate bei jedem Push: Artefakte gegen schema.yaml, STATUS.md
|
||||
# aktuell, Framework-Dateien unveraendert (Design 2026-08-11, Slice 1).
|
||||
# Braucht nur den Baum - bewusst ohne Token und ohne Netz.
|
||||
validate:
|
||||
stage: pruefen
|
||||
image: python:3.12-alpine
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
before_script:
|
||||
- pip install --quiet pyyaml
|
||||
script:
|
||||
- python3 scripts/validate.py
|
||||
- python3 scripts/gen_status.py --check
|
||||
- python3 scripts/pruefe_upstream_drift.py
|
||||
- python3 scripts/pruefe_prosa.py
|
||||
allow_failure: false
|
||||
|
||||
# Verbund-Prüfung (ADR-0012/0013): Gruppenliste vs. docs/components/,
|
||||
# Pointer-Praesenz, Meilenstein-/Prioritaetspflicht, Issue-Drift,
|
||||
# Git-Hygiene. Gleiche Regeln wie die Stillstandspruefung: geplant/von
|
||||
# Hand, rot = Alarm, Abbruch ohne Token.
|
||||
gruppenpruefung:
|
||||
stage: pruefen
|
||||
image: python:3.12-alpine
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
- if: $CI_PIPELINE_SOURCE == "web"
|
||||
script:
|
||||
- |
|
||||
if [ -z "$GITLAB_TOKEN" ]; then
|
||||
echo "GITLAB_TOKEN fehlt (Gruppen-Token mit read_api)."
|
||||
exit 1
|
||||
fi
|
||||
- python3 scripts/gruppenpruefung.py
|
||||
allow_failure: false
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
# AGENTS.md — Canonical Agent Instructions
|
||||
|
||||
Canonical instruction set for any coding agent working in this repository
|
||||
(Claude Code, GPT-OSS harnesses, others). `CLAUDE.md` points here.
|
||||
This file is loaded into every session — keep it short. Process details
|
||||
live in `WORKFLOW.md`; read that when a task begins, not preemptively.
|
||||
|
||||
Tradeoff: these rules bias toward caution over speed. For trivial tasks,
|
||||
use judgment — but say so.
|
||||
|
||||
## 1. Operating Rules
|
||||
|
||||
### Think before coding
|
||||
- State your assumptions explicitly. If uncertain, ask.
|
||||
- If multiple interpretations exist, present them — don't pick silently.
|
||||
- If a simpler approach exists, say so. Push back when warranted.
|
||||
- If something is unclear, stop. Name what's confusing. Ask.
|
||||
|
||||
### Simplicity first
|
||||
- Minimum code that solves the problem. Nothing speculative.
|
||||
- No features beyond what was asked. No abstractions for single-use code.
|
||||
- No "flexibility" or "configurability" that wasn't requested.
|
||||
- No error handling for impossible scenarios.
|
||||
- If you write 200 lines and it could be 50, rewrite it.
|
||||
- Test: "Would a senior engineer call this overcomplicated?" If yes, simplify.
|
||||
- Before writing new code, stop at the first rung that holds:
|
||||
needed at all? → codebase already has it? → stdlib? → platform-native?
|
||||
→ installed dependency? → one line? → only then: the minimum that works.
|
||||
(Ladder after ponytail, MIT.)
|
||||
- Never cut, at any rung: trust-boundary validation, data-loss handling,
|
||||
security, accessibility.
|
||||
- Lazy about the solution, never about reading the code first.
|
||||
|
||||
### Surgical changes
|
||||
- Touch only what you must. Match existing style, even if you'd differ.
|
||||
- Don't "improve" adjacent code, comments, or formatting.
|
||||
- Don't refactor things that aren't broken.
|
||||
- If you notice unrelated dead code, mention it — don't delete it.
|
||||
- Remove imports/variables/functions that YOUR changes made unused;
|
||||
leave pre-existing dead code alone unless asked.
|
||||
- Every changed line must trace directly to the request.
|
||||
|
||||
### Goal-driven execution
|
||||
- Transform tasks into verifiable goals:
|
||||
"fix the bug" → "write a test that reproduces it, then make it pass".
|
||||
- For multi-step work, state a brief plan: step → verify, step → verify.
|
||||
- A task is well-defined only if it names all four:
|
||||
**files, action, verify, done.** Missing one? The task is too vague — say so.
|
||||
|
||||
### Verification before completion
|
||||
- Never claim something works without evidence: a test run, command
|
||||
output, a rendered result. "Should work" is not a status.
|
||||
- Report every task/slice with exactly one status:
|
||||
`DONE` | `DONE_WITH_CONCERNS` | `NEEDS_CONTEXT` | `BLOCKED`.
|
||||
- Uncertainty is reported, never swallowed. Flag your shakiest calls.
|
||||
|
||||
## 2. Project Initialization (Gate 0)
|
||||
|
||||
At session start, read `PROJECT.md`. If it does not exist, initialization
|
||||
is your first task: before anything else, ask the Gate 0 questions defined
|
||||
in `WORKFLOW.md` — response language, size-S gate exception (yes/no),
|
||||
one-line project purpose, audience — write the answers to `PROJECT.md`,
|
||||
and have `validate.py` accept it. Never guess these answers; ask.
|
||||
|
||||
## 3. Workflow
|
||||
|
||||
For anything beyond a trivial change, read `WORKFLOW.md` and follow its
|
||||
gates. At task start, propose a size class (S/M/L); the human confirms
|
||||
(possibly batched later). **Never advance past a gate without explicit
|
||||
human approval** — sole exception: size-S tasks, and only if `PROJECT.md`
|
||||
explicitly grants that exception.
|
||||
|
||||
## 4. Repository Map
|
||||
|
||||
| Path | Purpose |
|
||||
|---|---|
|
||||
| `WORKFLOW.md` | Gate 0 (init) + Gates 1–5, size classes, debugging path, session handoff, refinement ritual |
|
||||
| `PROJECT.md` | Per-project answers from Gate 0: language, size-S exception, purpose, audience |
|
||||
| `STATUS.md` | Generated overview: open issues, active designs, recent ADRs — do not edit by hand |
|
||||
| `schema.yaml` | Frontmatter schema — single source of truth for artifact structure |
|
||||
| `docs/adr/` | Architecture Decision Records — binding; never edited, only superseded |
|
||||
| `docs/design/` | One design doc per undertaking; completed ones move to `done/` |
|
||||
| `docs/aar/` | Standalone After Action Reviews (incidents, major deviations only) |
|
||||
| `docs/issues/` | In-repo issues, one file each; status lives in frontmatter |
|
||||
| `docs/wiki/` | Wiki areas as folders, created on demand — rules in `docs/wiki/index.md` |
|
||||
| `docs/sources/` | Immutable original sources; wiki pages cite them — read-only for agents |
|
||||
| `scripts/` | Deterministic tooling: `validate.py`, `gen_status.py` |
|
||||
|
||||
Before proposing options (Gate 2), read the relevant ADRs and AARs first —
|
||||
past decisions and learnings are input, not trivia.
|
||||
|
||||
## 5. Artifact Rules
|
||||
|
||||
- All artifacts are standard Markdown with YAML frontmatter conforming to
|
||||
`schema.yaml`. Standard links only (`[text](path.md)`), no wikilinks.
|
||||
Diagrams as Mermaid. This keeps every artifact portable across LLMs,
|
||||
GitLab, and Obsidian.
|
||||
- Never invent frontmatter fields or status values. `validate.py` is
|
||||
authoritative; if it rejects your artifact, fix the artifact, not the
|
||||
validator.
|
||||
- Deterministic jobs (status generation, validation, link checks) are done
|
||||
by scripts, not by you. If a deterministic job lacks a script, propose
|
||||
one instead of doing it by inference.
|
||||
|
||||
<!-- projektabschnitt -->
|
||||
|
||||
## 6. Gruppenregeln (Projekt axion1337.chat)
|
||||
|
||||
Dieses Repo steuert die Gruppe `axion1337.chat`. Die Abschnitte 1–5
|
||||
oben sind neckbeard v0.1.1 und bleiben byte-treu (Baseline:
|
||||
`docs/sources/upstream/neckbeard-v0.1.1/`, Prüfung:
|
||||
`scripts/pruefe_upstream_drift.py`); §1 ist destilliert aus den
|
||||
wortgleich archivierten
|
||||
[Karpathy-Guidelines](docs/sources/regelwerk/karpathy-guidelines.md).
|
||||
**Änderungen an dieser Datei nur mit sorb abgestimmt.** Dieser
|
||||
Abschnitt gilt für jede Session in allen Repos der Gruppe;
|
||||
Komponenten-Repos tragen nur Projektspezifika plus einen Pointer
|
||||
hierher ([ADR-0013](docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)).
|
||||
Ohne Lab-Zugang: dieses Repo ist als Push-Mirror unter
|
||||
`https://rohana.axion1337.de/sorb/management` lesbar — pushen dorthin ist tabu.
|
||||
|
||||
### Quelle der Wahrheit & Mirror-Topologie
|
||||
|
||||
- `git.lab/axion1337.chat/*` ist kanonisch; Gitea/rohana wird per
|
||||
Push-Mirror beliefert und bleibt Flux-Quelle, Registry und
|
||||
Release-Download ([ADR-0001](docs/adr/0001-gitlab-kanonisch-push-mirror.md),
|
||||
[ADR-0004](docs/adr/0004-site-to-site-vpn-hetzner-lab.md)). Die
|
||||
Flux-Quelle **nicht auf git.lab „geradeziehen"** — die Produktion darf
|
||||
nicht an der Lab-Verfügbarkeit hängen.
|
||||
- **Nie direkt zu Gitea pushen** (der Mirror überschreibt per Force).
|
||||
Landet doch ein Commit dort: Kanonisierungs-Verfahren in
|
||||
[verfahren/deploy-uebergabe.md](docs/wiki/deployment/deploy-uebergabe.md).
|
||||
- Einzige bewusste Ausnahme: der TURN-Rotations-CronJob schreibt nach
|
||||
Gitea; der tägliche CI-Job `canonize_rotation` holt es zurück. Seine
|
||||
rote Pipeline **ist** der Alarm — es gibt bewusst keinen zweiten Meldeweg.
|
||||
|
||||
### Issues & Board
|
||||
|
||||
- `docs/issues/` ist kanonisch für den Management-Scope
|
||||
([ADR-0012](docs/adr/0012-issues-im-repo-gitlab-als-spiegel.md));
|
||||
GitLab ist bespiegelte Ansicht. Jedes Issue trägt genau einen
|
||||
Meilenstein (M1–M5) und genau eine Priorität — das Schema erzwingt
|
||||
beides. Zeitkritisches trägt ein `due`-Datum, nicht „bald".
|
||||
- **WIP-Limit 2** (Validator-Regel). Die Zusage-Status `next` und
|
||||
`in-progress` vergibt **nur sorb**; Sessions bilden ab (`waiting` mit
|
||||
`wartegrund`, Erledigtes `done` mit Begründung im Issue-Commit),
|
||||
sagen aber nichts zu.
|
||||
- **ADR-Pflicht** bei Architektur-/Prozessentscheidungen und **jeder
|
||||
dauerhaften Ausnahme von einer Regel** — eine Ausnahme nur zu
|
||||
dokumentieren statt sie zu entscheiden, ist ein Fehler.
|
||||
|
||||
### Secrets & Credentials
|
||||
|
||||
- Token-/Secret-Werte **niemals anzeigen, loggen oder in Dateien
|
||||
echoen** — anzeigen = Exposure = Rotation. Referenz nur über
|
||||
Dateipfade (z. B. `~/.config/gitlab-lab/token`) oder maskierte
|
||||
CI-Variablen. Die Trennung ist „Credential vs. Config":
|
||||
nicht-geheime Konfiguration wird normal committet.
|
||||
|
||||
### Commit-Konventionen
|
||||
|
||||
- Nachrichten auf Englisch, Conventional-Stil; der Betreff sagt *was*,
|
||||
der Rumpf *warum*.
|
||||
- Autor- **und** Committer-Datum auf 12:00:00 UTC des laufenden Tages;
|
||||
kanonische Autor-Identität. Historien-Rewrites nur mit
|
||||
alt→neu-Zuordnung
|
||||
([ADR-0009](docs/adr/0009-commit-konventionen-und-historien-anonymisierung.md),
|
||||
Tabelle: [shared/commit-zuordnung-2026-08-07.md](docs/sources/migration/commit-zuordnung-2026-08-07.md)).
|
||||
- ⚠️ Das schützt nur die Git-Historie; Plattform-Zeitstempel (Push,
|
||||
Issues, Pipelines, Pakete) tragen die echte Uhrzeit (ADR-0009).
|
||||
|
||||
### Redlichkeit
|
||||
|
||||
- Verifiziert (Messung/Konsole) klar von Vermutung trennen;
|
||||
Korrelation ≠ Kausalität — ein plausibler Verdacht ist kein Befund.
|
||||
- Config-Dateien chirurgisch editieren, **nie re-dumpen**; vor dem Push
|
||||
validieren (`docker compose config`, YAML-Parse).
|
||||
- Fehlschläge und übersprungene Schritte benennen, nicht glätten —
|
||||
„fertig" heißt verifiziert (deckungsgleich mit §1).
|
||||
@@ -1,233 +1 @@
|
||||
# CLAUDE.md — übergreifende Arbeitskonventionen (kanonisch)
|
||||
|
||||
Diese Datei gilt für **jede Claude-/Agenten-Session in allen Projekten** der
|
||||
Gruppe (axion1337.chat-Stack, ThreadNet-Repos, CFGMON/threadnet-operating,
|
||||
Homelab). Projekt-Repos haben eigene CLAUDE.mds für ihre Spezifika (z. B. die
|
||||
ESS-/Flux-Details in „ThreadNet Server Suite" = `axion1337.chat-gitops`) — bei
|
||||
Widerspruch gilt für Arbeitsweise und Prozess **diese** Datei.
|
||||
|
||||
> **Für Sessions ohne Lab-Zugang** (CFGMON, MATRIX, …): dieses Repo ist als
|
||||
> Push-Mirror unter `https://rohana.axion1337.de/sorb/management` von überall
|
||||
> **lesbar** — dort diese Datei und die ADRs nachschlagen. Nur pushen ist tabu.
|
||||
>
|
||||
> 📋 **Kopierbare Kurzfassungen zum Voranstellen:**
|
||||
> [verfahren/textbloecke.md](verfahren/textbloecke.md) — Session-Start, Host-Session,
|
||||
> Deploy-Übergabe, Abschluss, Entscheidungsvorlage. Diese Datei hier bleibt die
|
||||
> Quelle; die Bausteine verweisen nur darauf.
|
||||
|
||||
## Projektrealitäten (Stand 2026-08-01)
|
||||
|
||||
**Das Lab ist die Quelle der Wahrheit** ([ADR-0002](decisions/0002-issues-und-management-ins-lab.md)):
|
||||
|
||||
- Kanonische Repos liegen auf `git.lab/axion1337.chat/*` (nur im Lab/VPN
|
||||
auflösbar). Gitea/rohana wird per **Push-Mirror** beliefert und bleibt
|
||||
Flux-Source, Container-/npm-Registry und Release-Download
|
||||
([ADR-0001](decisions/0001-gitlab-kanonisch-push-mirror.md)).
|
||||
- **Warum überhaupt zwei Orte — und warum das kein Altbestand ist:** Auf git.lab
|
||||
liegen die *Baupläne*, auf Gitea eine Kopie, die der Cluster **ohne verfügbares
|
||||
Lab** erreicht. Der Hetzner-Cluster muss sich bauen und neu ausrollen lassen,
|
||||
wenn das Homelab aus ist, im Umbau steckt oder niemand zu Hause ist — er darf
|
||||
deshalb nicht von einem Host abhängen, der nur im Lab antwortet.
|
||||
⚠️ **Die Flux-Quelle nicht „geradeziehen"** auf git.lab: Das sähe aufgeräumter
|
||||
aus und würde die Verfügbarkeit der Produktion an das Lab koppeln — genau das,
|
||||
was die Trennung verhindert.
|
||||
- **Nie direkt zu Gitea pushen** (gespiegelte Repos) — der Mirror überschreibt
|
||||
per Force.
|
||||
- **Gespiegelt wird nur die Gruppe `axion1337.chat`** (die fünf Produkt-Repos und
|
||||
`management`). Die Gruppe **`homelab`** (`docs`, `wiki`, `wiki-bookstack`) hat
|
||||
bewusst **keine Mirrors**: Sie beschreibt und konfiguriert ausschließlich
|
||||
Lab-Infrastruktur, und seit dem Site-to-Site-VPN
|
||||
([ADR-0004](decisions/0004-site-to-site-vpn-hetzner-lab.md)) erreichen auch
|
||||
Host-Sessions git.lab direkt — Tunnel einschalten genügt. Betriebslehren, die
|
||||
von außen lesbar sein müssen, gehören deshalb in die **AARs** unter
|
||||
`verfahren/aar/` (dieses Repo ist gespiegelt), nicht nur in die READMEs der
|
||||
Lab-Repos.
|
||||
- Landet doch ein Commit auf Gitea (z. B. aus einer Host-Session ohne Lab-Route):
|
||||
**Kanonisierungs-Verfahren** in
|
||||
[verfahren/deploy-uebergabe.md](verfahren/deploy-uebergabe.md) — `.patch`
|
||||
von Gitea ziehen, `git am` (erhält Autorschaft), Push über git.lab.
|
||||
- **Issues leben auf git.lab.** Die alten Gitea-Issues sind geschlossen und
|
||||
verweisen dorthin. ⚠️ gitops-Nummern haben sich beim Umzug verschoben
|
||||
(Gitea zählte PRs mit; z. B. Gitea#48 → GitLab#46) — alte „gitops#N"-Verweise
|
||||
meinen die Gitea-Nummer; verbindlich ist der Migrations-Fußtext im Issue.
|
||||
- **Ausnahme** (bewusst entschieden, nur noch eine): der
|
||||
TURN-Rotations-CronJob schreibt weiter nach Gitea, weil er im Cluster läuft und
|
||||
git.lab nicht erreicht.
|
||||
**Die Rotation nicht von Hand nachziehen und den PR nie auf Gitea mergen** —
|
||||
das erledigt seit 2026-08-02 der geplante CI-Job `canonize_rotation` im
|
||||
gitops-Repo täglich von git.lab aus. Scheitert er, bleibt die Pipeline rot;
|
||||
diese rote Pipeline **ist** der Alarm, einen zusätzlichen Termin gibt es
|
||||
bewusst nicht.
|
||||
- **Dokumentation** ([ADR-0006](decisions/0006-wikis-konsolidieren-docusaurus.md)):
|
||||
Das gitops-Wiki liegt seit 2026-08-02 auf git.lab (*Wiki*-Reiter im Projekt);
|
||||
⚠️ der `wiki`-**Branch** im gitops-Repo ist ein überholter Mai-Abzug von `docs/`
|
||||
und nicht die gepflegte Fassung. Alle Quellen zusammen erscheinen unter
|
||||
**axionwiki.lab** ([`homelab/wiki`](https://git.lab/homelab/wiki), Docusaurus) —
|
||||
Inhalte werden beim Bau geholt, **Änderungen gehören ins Quell-Repo**.
|
||||
|
||||
## Arbeitsframework ([ADR-0005](decisions/0005-pm-framework-kanban.md))
|
||||
|
||||
Kanban-Rückgrat mit leichten Scrum-Elementen:
|
||||
|
||||
- **Alles Offene ist ein Issue** — host-/infra-Scope hier im management-Projekt
|
||||
(`host:`-Labels, alte IDs wie `CFGMON-01` bleiben im Titel), Projekt-Scope im
|
||||
jeweiligen Projekt. Kein neues Backlog-Markdown anlegen; `hosts/`/`shared/`
|
||||
sind nur Bestand + Historie.
|
||||
- **Status über Labels**, genau eins pro Issue: `status:next` (die einzige
|
||||
Zusage), `status:doing` (**WIP-Limit 2** — auch sessionübergreifend zu
|
||||
verteidigen), `status:wartet` (nur mit benanntem Grund). Ohne Label = Backlog.
|
||||
- **ADR-Pflicht** ([decisions/](decisions/)) bei Architektur-/Prozess-
|
||||
entscheidungen und **jeder dauerhaften Ausnahme von einer Regel**. Eine
|
||||
Ausnahme nur zu dokumentieren statt sie als Entscheidung vorzulegen, ist ein
|
||||
Fehler.
|
||||
- **Deploy-Übergaben** („einer baut, ein anderer rollt aus") laufen über das
|
||||
Issue-Template und die Pflichtfelder in
|
||||
[verfahren/deploy-uebergabe.md](verfahren/deploy-uebergabe.md) — das ist
|
||||
unsere Definition of Done für Deployments. Nach Deploys mit Übergabe und nach
|
||||
Incidents: **AAR** ([verfahren/aar/](verfahren/aar/), Vorlage liegt daneben).
|
||||
- Prioritäten über `priority:*`; Zeitkritisches bekommt ein **Datum** im Issue,
|
||||
nicht „bald".
|
||||
- **Der Titel trägt keine Priorität.** Präfixe wie `[HIGH]`/`[MEDIUM]`/`[LOW]`
|
||||
gehören nicht in den Titel — die Priorität steht im Label, und zwar nur dort.
|
||||
Alte Kennungen wie `CFGMON-01` bleiben, die benennen den Gegenstand, nicht die
|
||||
Dringlichkeit.
|
||||
⚠️ Der Grund ist keine Ästhetik: Aus der Gitea-Migration trugen 34 Issues ein
|
||||
Präfix, davon **zwei mit einer anderen Aussage als ihr Label** — wer nach Titel
|
||||
sortierte, bekam ein anderes Bild als wer nach Label sortierte. Zwei Wahrheiten
|
||||
über dieselbe Sache sind schlimmer als eine unvollständige. Bereinigt 2026-08-06.
|
||||
- **Jedes Issue gehört zu genau einem Meilenstein** (Gruppen-Milestones M1–M4,
|
||||
siehe [roadmap.md](roadmap.md)). Label und Meilenstein beantworten verschiedene
|
||||
Fragen: `priority:*` sagt **wie dringend**, der Meilenstein sagt **worauf es
|
||||
einzahlt**. Ein Issue ohne Meilenstein taucht in keiner Roadmap-Ansicht auf und
|
||||
ist damit praktisch unsichtbar — es existiert nur noch für den, der es angelegt
|
||||
hat.
|
||||
M1–M4 haben **bewusst kein Enddatum**: Sie bündeln, sie simulieren keinen
|
||||
Termindruck. Termindruck steht als Datum am einzelnen Issue.
|
||||
|
||||
## Secrets & Credentials
|
||||
|
||||
- **Token-/Secret-Werte niemals anzeigen, loggen oder in Dateien echoen** —
|
||||
anzeigen = Exposure = Rotation. Echte Credentials tippt/legt sorb selbst an;
|
||||
Sessions referenzieren sie nur über Dateipfade (z. B.
|
||||
`~/.config/gitlab-lab/token`) oder maskierte CI-Variablen.
|
||||
- Nicht-geheime Konfiguration wird direkt geschrieben und committet — die
|
||||
Trennung ist „Credential vs. Config", nicht „alles über den Menschen".
|
||||
|
||||
## Commit-Konventionen (seit 2026-08-07)
|
||||
|
||||
Gilt für **alle** Repos der Gruppe `axion1337.chat` und die ThreadNet-Dienste.
|
||||
|
||||
- **Nachrichten auf Englisch**, Conventional-Commit-Stil: `feat:`, `fix:`,
|
||||
`docs:`, `chore:`, `ci:`, `refactor:`. Der Betreff sagt *was*, der Rumpf *warum*.
|
||||
- **Zeitstempel anonymisieren.** Autor- **und** Committer-Datum werden auf
|
||||
**12:00:00 UTC des laufenden Tages** gesetzt, damit sich aus der Historie keine
|
||||
persönlichen Arbeitszeiten ablesen lassen:
|
||||
|
||||
```bash
|
||||
export GIT_AUTHOR_DATE="$(date -u +%Y-%m-%d)T12:00:00Z" \
|
||||
GIT_COMMITTER_DATE="$(date -u +%Y-%m-%d)T12:00:00Z"
|
||||
git commit -m "…"
|
||||
```
|
||||
|
||||
⚠️ **Beide Variablen setzen.** Nur `GIT_AUTHOR_DATE` zu setzen bringt nichts —
|
||||
`git log` zeigt zwar das Autordatum, das Committer-Datum bleibt aber im Objekt
|
||||
und ist über `git log --format=%cd` und in jeder Weboberfläche sichtbar.
|
||||
|
||||
📎 Die Umstellung der Alt-Historie am 2026-08-07 hat 251 Commits neue SHAs
|
||||
gegeben. Ältere Verweise bleiben über
|
||||
[`shared/commit-zuordnung-2026-08-07.md`](shared/commit-zuordnung-2026-08-07.md)
|
||||
auflösbar — **statt** geschriebene Issue-Kommentare nachträglich zu ändern. Wer
|
||||
eine SHA nicht findet, hat einen Commit von vor der Grenze vor sich; der gilt
|
||||
unverändert.
|
||||
|
||||
⚠️ **Das schützt nur die Git-Historie.** Push-Zeiten, Issue- und
|
||||
Kommentar-Zeitstempel, Pipeline-Läufe und Paket-Veröffentlichungen tragen
|
||||
weiterhin die echte Uhrzeit und liegen im selben GitLab bzw. auf dem
|
||||
öffentlichen Gitea-Spiegel. Wer daraus wirklich keine Muster ableitbar haben
|
||||
will, muss dort ansetzen — die Commit-Datumsregel allein reicht dafür nicht.
|
||||
|
||||
## Redlichkeit & gelebte Lehren
|
||||
|
||||
- **Aussagen mit Quelle:** Verifiziert (Messung/Konsole) klar von Vermutung
|
||||
trennen; nicht selbst Geprüftes als solches kennzeichnen. Korrelation ≠
|
||||
Kausalität — ein plausibler Verdacht ist kein Befund.
|
||||
- **Config-Dateien textuell/chirurgisch editieren, nie re-dumpen** (YAML/JSON
|
||||
neu serialisieren hat zweimal real Schaden angerichtet). Compose-/YAML-
|
||||
Änderungen vor dem Push validieren (`docker compose config`, YAML-Parse).
|
||||
- Fehlschläge und übersprungene Schritte werden benannt, nicht geglättet;
|
||||
„fertig" heißt verifiziert.
|
||||
|
||||
---
|
||||
name: karpathy-guidelines
|
||||
description: Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# Karpathy Guidelines
|
||||
|
||||
Behavioral guidelines to reduce common LLM coding mistakes, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls.
|
||||
|
||||
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
||||
|
||||
## 1. Think Before Coding
|
||||
|
||||
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
||||
|
||||
Before implementing:
|
||||
- State your assumptions explicitly. If uncertain, ask.
|
||||
- If multiple interpretations exist, present them - don't pick silently.
|
||||
- If a simpler approach exists, say so. Push back when warranted.
|
||||
- If something is unclear, stop. Name what's confusing. Ask.
|
||||
|
||||
## 2. Simplicity First
|
||||
|
||||
**Minimum code that solves the problem. Nothing speculative.**
|
||||
|
||||
- No features beyond what was asked.
|
||||
- No abstractions for single-use code.
|
||||
- No "flexibility" or "configurability" that wasn't requested.
|
||||
- No error handling for impossible scenarios.
|
||||
- If you write 200 lines and it could be 50, rewrite it.
|
||||
|
||||
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
||||
|
||||
## 3. Surgical Changes
|
||||
|
||||
**Touch only what you must. Clean up only your own mess.**
|
||||
|
||||
When editing existing code:
|
||||
- Don't "improve" adjacent code, comments, or formatting.
|
||||
- Don't refactor things that aren't broken.
|
||||
- Match existing style, even if you'd do it differently.
|
||||
- If you notice unrelated dead code, mention it - don't delete it.
|
||||
|
||||
When your changes create orphans:
|
||||
- Remove imports/variables/functions that YOUR changes made unused.
|
||||
- Don't remove pre-existing dead code unless asked.
|
||||
|
||||
The test: Every changed line should trace directly to the user's request.
|
||||
|
||||
## 4. Goal-Driven Execution
|
||||
|
||||
**Define success criteria. Loop until verified.**
|
||||
|
||||
Transform tasks into verifiable goals:
|
||||
- "Add validation" → "Write tests for invalid inputs, then make them pass"
|
||||
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
||||
- "Refactor X" → "Ensure tests pass before and after"
|
||||
|
||||
For multi-step tasks, state a brief plan:
|
||||
```
|
||||
1. [Step] → verify: [check]
|
||||
2. [Step] → verify: [check]
|
||||
3. [Step] → verify: [check]
|
||||
```
|
||||
|
||||
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
|
||||
|
||||
---
|
||||
|
||||
*Der Karpathy-Block oben ist wortgleich aus der gitops-CLAUDE.md übernommen und
|
||||
darf nicht bearbeitet werden (stehende Regel von sorb). Änderungen an dieser
|
||||
Datei insgesamt: nur mit sorb abgestimmt — sie ist die gemeinsame
|
||||
Arbeitsgrundlage aller Sessions.*
|
||||
Read AGENTS.md — the canonical instruction file for this repository. All rules live there.
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
---
|
||||
type: project
|
||||
language: de
|
||||
size_s_exception: true
|
||||
purpose: "Steuerungsrepo der Gruppe axion1337.chat: Roadmap, Entscheidungen, Verfahren und Host-/Infrastrukturwissen für die fünf ThreadNet-Komponenten und das Lab, das sie betreibt."
|
||||
audience: "sorb und Agenten-Sessions (auch Host-Sessions ohne Lab-Zugang, lesend über den Gitea-Mirror); keine weiteren Menschen."
|
||||
---
|
||||
|
||||
# PROJECT.md — Gate-0-Antworten
|
||||
|
||||
Beantwortet von sorb am 2026-08-11 (Session 2 des Neckbeard-Feldtests).
|
||||
|
||||
- **Antwortsprache:** Deutsch. Commit-Messages bleiben englisch
|
||||
(Commit-Konvention der Gruppe, seit 2026-08-07).
|
||||
- **Size-S-Ausnahme:** gewährt — triviale Ein-Datei-Änderungen ohne
|
||||
Einzelfreigabe; M und L stoppen immer.
|
||||
- **Zweck:** siehe Frontmatter.
|
||||
- **Publikum:** Owner plus Agenten-Sessions; keine weiteren Menschen.
|
||||
Für die spätere Wiki-Pflicht heißt das: keine fremdgerichteten
|
||||
Bereiche (user-guide) verpflichtend.
|
||||
@@ -3,7 +3,7 @@
|
||||
Steuerungs-Repo für alles über den einzelnen Projekten: Visionen, Roadmap,
|
||||
Entscheidungen (ADR), Arbeitsverfahren, AARs — und der Bestand der Hosts.
|
||||
Framework: **Kanban-Rückgrat mit leichten Scrum-Elementen**, begründet und
|
||||
im Detail festgelegt in [ADR-0005](decisions/0005-pm-framework-kanban.md).
|
||||
im Detail festgelegt in [ADR-0005](docs/adr/0005-pm-framework-kanban.md).
|
||||
|
||||
*(Bis 2026-08-01 hieß dieses Repo `Backlogs` und führte offene Punkte als
|
||||
Markdown — die leben jetzt als Issues, siehe unten.)*
|
||||
@@ -12,16 +12,16 @@ Markdown — die leben jetzt als Issues, siehe unten.)*
|
||||
|
||||
**Kanonisch lebt dieses Repo auf `git.lab`** (`axion1337.chat/management`, nur im
|
||||
Lab bzw. via VPN erreichbar — das Lab ist die Quelle der Wahrheit,
|
||||
[ADR-0002](decisions/0002-issues-und-management-ins-lab.md)).
|
||||
[ADR-0002](docs/adr/0002-issues-und-management-ins-lab.md)).
|
||||
`rohana.axion1337.de/sorb/management` ist ein **Push-Mirror**: git.lab
|
||||
überschreibt ihn bei jedem Push per Force. Deshalb **nie direkt zu Gitea
|
||||
pushen** — solche Commits gehen beim nächsten Mirror-Lauf verloren (Rettung:
|
||||
`.patch` von Gitea ziehen + `git am`, siehe
|
||||
[Kanonisierung](verfahren/deploy-uebergabe.md)).
|
||||
[Kanonisierung](docs/wiki/deployment/deploy-uebergabe.md)).
|
||||
|
||||
**Keine Ausnahmen mehr.** Die **Deploy-Übergabe-Issues** liefen bis 2026-08-02 auf
|
||||
dem Gitea-Tracker, weil Hosts außerhalb des Labs `git.lab` nicht erreichten. Mit dem
|
||||
Site-to-Site-VPN ([ADR-0004](decisions/0004-site-to-site-vpn-hetzner-lab.md)) ist der
|
||||
Site-to-Site-VPN ([ADR-0004](docs/adr/0004-site-to-site-vpn-hetzner-lab.md)) ist der
|
||||
Grund entfallen — bei eingeschaltetem Tunnel erreicht CFGMON git.lab. Sie sind
|
||||
umgezogen (LABNET-03), der Gitea-Tracker ist leer, die Vorlage liegt als
|
||||
GitLab-Issue-Template. **Alle Issues leben auf git.lab.**
|
||||
@@ -34,12 +34,12 @@ GitLab-Issue-Template. **Alle Issues leben auf git.lab.**
|
||||
| `vision/` | Eine Vision je Linie: Community (axion1337.chat), Tool (ThreadNet), Plattform (Homelab) |
|
||||
| `roadmap.md` | Linien, Meilenstein-Kandidaten, Kadenz — GitLab-Milestones halten den Stand |
|
||||
| `decisions/` | ADRs — Pflicht bei Architekturentscheidungen **und dauerhaften Ausnahmen** |
|
||||
| `verfahren/` | Wie wir arbeiten: [Deploy-Übergabe/DoD](verfahren/deploy-uebergabe.md), [Refinement & Retro](verfahren/refinement.md), [AARs](verfahren/aar/), Werkzeuge |
|
||||
| `hosts/`, `shared/` | **Bestand + Historie** je Host/Thema — u. a. [Branding](shared/branding.md) (Marke, Paletten, wo welches Theme eingestellt ist); offene Punkte sind Issues |
|
||||
| `verfahren/` | Wie wir arbeiten: [Deploy-Übergabe/DoD](docs/wiki/deployment/deploy-uebergabe.md), [Refinement & Retro](docs/wiki/admin/refinement.md), AARs (`docs/aar/`), Werkzeuge |
|
||||
| `hosts/`, `shared/` | **Bestand + Historie** je Host/Thema — u. a. [Branding](docs/wiki/architecture/branding.md) (Marke, Paletten, wo welches Theme eingestellt ist); offene Punkte sind Issues |
|
||||
|
||||
Gelesen wird das alles auch gebündelt unter **[axionwiki.lab](https://axionwiki.lab)** —
|
||||
dort stehen Plattform-Wiki, Homelab-Doku und dieses Repo nebeneinander
|
||||
([ADR-0006](decisions/0006-wikis-konsolidieren-docusaurus.md), Konfiguration in
|
||||
([ADR-0006](docs/adr/0006-wikis-konsolidieren-docusaurus.md), Konfiguration in
|
||||
[`homelab/wiki`](https://git.lab/homelab/wiki)). **Geändert wird immer hier, nie dort.**
|
||||
|
||||
## Das Backlog: Issues + Board
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# STATUS
|
||||
|
||||
<!-- Generated by scripts/gen_status.py — do not edit. -->
|
||||
|
||||
## Issues (36 open, 0 closed)
|
||||
|
||||
Verteilung: M1 9 · M2 25 · M4 2
|
||||
|
||||
| Issue | Status | Meilenstein | Priorität | Title |
|
||||
|---|---|---|---|---|
|
||||
| [0001](docs/issues/0001-matrix-03-www-matrix-axion1337-de-ist.md) | open | M2 | low | MATRIX-03: www.matrix.axion1337.de ist überflüssig |
|
||||
| [0002](docs/issues/0002-game-01-host-von-cfgmon-aus-nicht-erreichbar-2.md) | waiting | M1 | medium | GAME-01: Host von CFGMON aus nicht erreichbar, 2 Prometheus-Targets down |
|
||||
| [0003](docs/issues/0003-game-02-www-game-axion1337-de-ist-ueberfluessig.md) | open | M2 | low | GAME-02: www.game.axion1337.de ist überflüssig |
|
||||
| [0004](docs/issues/0004-overmind-02-e1000e-nic-hang-beobachtung-nach.md) | waiting | M1 | low | OVERMIND-02: e1000e-NIC-Hang — Beobachtung nach EEE-Fix + Firmware-Update |
|
||||
| [0005](docs/issues/0005-zone-01-ionos-default-records-bereinigen-www.md) | open | M2 | low | ZONE-01: IONOS-Default-Records bereinigen (www-Paare, tote Mail-Sätze) |
|
||||
| [0006](docs/issues/0006-zone-02-apex-dmarc-ist-p-none-und-schuetzt.md) | open | M1 | low | ZONE-02: Apex-DMARC ist p=none und schützt nichts |
|
||||
| [0007](docs/issues/0007-cfgmon-01-zertifikatserneuerung-braucht-offene.md) | next | M1 | high | CFGMON-01: Zertifikatserneuerung braucht offene Ports — zeitkritisch ab 2026-09-28 |
|
||||
| [0008](docs/issues/0008-cfgmon-03-prometheus-remote-write-und-loki.md) | waiting | M1 | medium | CFGMON-03: Prometheus-Remote-Write und Loki öffentlich ohne Auth — Weg A, nachgelagerte Prüfung |
|
||||
| [0009](docs/issues/0009-cfgmon-04-grafana-admin-credentials-aus-env.md) | open | M2 | low | CFGMON-04: Grafana-Admin-Credentials aus .env gelten nicht für die HTTP-API |
|
||||
| [0010](docs/issues/0010-cfgmon-09-gitea-backups-off-host-borg-storage.md) | open | M1 | medium | CFGMON-09: Gitea-Backups off-host (Borg/Storage Box) — Backup-Cron ist DEAKTIVIERT |
|
||||
| [0014](docs/issues/0014-cfgmon-14-root-zugang-ueber-die-docker-gruppe.md) | waiting | M2 | low | CFGMON-14: Root-Zugang über die docker-Gruppe umgeht sudo und hinterlässt keine Spur |
|
||||
| [0015](docs/issues/0015-cfgmon-15-token-hygiene-einmal-tokens-der.md) | next | M2 | medium | CFGMON-15: Token-Hygiene — Einmal-Tokens der LABNET-02-Nacht widerrufen |
|
||||
| [0018](docs/issues/0018-doc-01-wiki-rollout-abschliessen-ci-freigaben.md) | open | M2 | low | DOC-01: Wiki-Rollout abschließen — CI-Freigaben, Zeitplan, Dokploy-Stack, wiki.lab |
|
||||
| [0019](docs/issues/0019-doc-02-veralteten-wiki-branch-im-gitops-repo.md) | open | M2 | low | DOC-02: Veralteten `wiki`-Branch im gitops-Repo entfernen? |
|
||||
| [0020](docs/issues/0020-doc-03-wiki-oberflaeche-entscheiden-docusaurus.md) | next | M2 | medium | DOC-03: Wiki-Oberfläche entscheiden — Docusaurus oder BookStack |
|
||||
| [0021](docs/issues/0021-overmind-03-windows-build-vm-verschwindet-ci.md) | waiting | M2 | medium | OVERMIND-03: Windows-Build-VM verschwindet — CI kann sie nur starten, nicht anlegen |
|
||||
| [0022](docs/issues/0022-build-01-macos-client-reproduzierbar-bauen.md) | open | M4 | low | BUILD-01: macOS-Client reproduzierbar bauen — aktuell nur manuell auf sorbs Mac |
|
||||
| [0023](docs/issues/0023-doc-04-navbar-logo-im-docusaurus-wiki-wird.md) | open | M2 | low | DOC-04: Navbar-Logo im Docusaurus-Wiki wird ausgeliefert, ist aber nicht sichtbar |
|
||||
| [0024](docs/issues/0024-wiki-hostname-klaeren-wiki-lab-oder-axionwiki.md) | open | M2 | low | Wiki-Hostname klären: wiki.lab oder axionwiki.lab? |
|
||||
| [0025](docs/issues/0025-deploy-uebergabe-cve-alarme-aggregiert-receiver.md) | waiting | M1 | medium | Deploy-Übergabe: CVE-Alarme aggregiert + Receiver-Robustheit (gitops#51, ff87cb2) |
|
||||
| [0027](docs/issues/0027-audit-01-acht-widersprueche-aus-dem-labnet-02.md) | waiting | M2 | medium | AUDIT-01: Acht Widersprüche aus dem LABNET-02-Nachlauf (Selbst-Audit CFGMON-Session) |
|
||||
| [0028](docs/issues/0028-mirror-01-ein-ausfall-der-push-mirrors-bleibt.md) | open | M2 | low | MIRROR-01: Ein Ausfall der Push-Mirrors bleibt unbemerkt — Produktion friert still ein |
|
||||
| [0029](docs/issues/0029-ui-harmonisieren-gleiche-farben-und-formen.md) | open | M4 | medium | UI harmonisieren: gleiche Farben und Formen über alle Oberflächen |
|
||||
| [0030](docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md) | open | M1 | medium | Der Restore ist nie geprobt — Sicherungen sind bisher eine Vermutung |
|
||||
| [0031](docs/issues/0031-stillstandspruefung-gitea-token-und-authentik.md) | open | M1 | low | Stillstandsprüfung: GITEA_TOKEN und Authentik-Teil nachziehen |
|
||||
| [0032](docs/issues/0032-gameserver-hat-keinen-push-mirror-und-auf-gitea.md) | open | M2 | medium | gameserver hat keinen Push-Mirror — und auf Gitea liegt ein anderer Stand |
|
||||
| [0033](docs/issues/0033-overmind-01-element-desktop-build-lab-registry.md) | open | M2 | low | OVERMIND-01 — element-desktop-build von rohana in die Lab-Registry umziehen |
|
||||
| [0034](docs/issues/0034-cfgmon-11-gitea-ci-rueckbau-abschliessen.md) | open | M2 | medium | CFGMON-11 — Gitea-CI-Rückbau abschließen (sicher rückbaubare Schritte) |
|
||||
| [0035](docs/issues/0035-rollout-agents-pointer-axion1337-chat-gitops.md) | open | M2 | medium | Rollout Gruppenregeln-Pointer: `axion1337.chat-gitops` |
|
||||
| [0036](docs/issues/0036-rollout-agents-pointer-threadnet-web.md) | open | M2 | medium | Rollout Gruppenregeln-Pointer: `ThreadNet-Web` |
|
||||
| [0037](docs/issues/0037-rollout-agents-pointer-threadnet-call.md) | open | M2 | medium | Rollout Gruppenregeln-Pointer: `threadnet-call` |
|
||||
| [0038](docs/issues/0038-rollout-agents-pointer-thread-net-git.md) | open | M2 | medium | Rollout Gruppenregeln-Pointer: `thread-net-git` |
|
||||
| [0039](docs/issues/0039-rollout-agents-pointer-threadnet-operating.md) | open | M2 | medium | Rollout Gruppenregeln-Pointer: `threadnet-operating` |
|
||||
| [0040](docs/issues/0040-neckbeard-rueckmeldungen-einreichen.md) | open | M2 | low | neckbeard-Rückmeldungen aus dem Feldtest einreichen |
|
||||
| [0041](docs/issues/0041-wartegrund-der-importierten-waiting-issues.md) | open | M2 | low | wartegrund der 7 importierten waiting-Issues präzisieren |
|
||||
| [0042](docs/issues/0042-migration-in-betrieb-nehmen-push-spiegel-schedule.md) | open | M2 | high | Migration in Betrieb nehmen: Push, erster Spiegel-Lauf, CI-Schedule |
|
||||
|
||||
## Active design docs (0)
|
||||
|
||||
_none active_
|
||||
|
||||
## ADRs (13)
|
||||
|
||||
| ADR | Status | Title |
|
||||
|---|---|---|
|
||||
| [0001](docs/adr/0001-gitlab-kanonisch-push-mirror.md) | accepted | 0001 — git.lab ist kanonisch, Gitea wird per Push-Mirror beliefert |
|
||||
| [0002](docs/adr/0002-issues-und-management-ins-lab.md) | accepted | 0002 — Issues und Management-Repo ziehen ins Lab („das Lab ist die Quelle der Wahrheit") |
|
||||
| [0003](docs/adr/0003-cve-meldeweg-aggregiert.md) | accepted | 0003 — CVE-Meldeweg: aggregierte Alarme, eigener Security-Raum, gleicher Bot |
|
||||
| [0004](docs/adr/0004-site-to-site-vpn-hetzner-lab.md) | accepted | 0004 — Site-to-Site-VPN Hetzner-Projektnetz ↔ Lab, schaltbar über die UDM |
|
||||
| [0005](docs/adr/0005-pm-framework-kanban.md) | accepted | 0005 — Projektmanagement: Kanban-Rückgrat mit leichten Scrum-Elementen |
|
||||
| [0006](docs/adr/0006-wikis-konsolidieren-docusaurus.md) | accepted | 0006 — Wikis ins Lab konsolidieren, Docusaurus als gemeinsame Lesefläche |
|
||||
| [0007](docs/adr/0007-wiki-oberflaeche-docusaurus-vs-bookstack.md) | proposed | 0007 — Wiki-Oberfläche: Docusaurus läuft, BookStack als Gegenentwurf |
|
||||
| [0008](docs/adr/0008-agenten-sessions-root-aequivalent.md) | accepted | 0008 — Agenten-Sessions auf CFGMON laufen root-äquivalent über die docker-Gruppe |
|
||||
| [0009](docs/adr/0009-commit-konventionen-und-historien-anonymisierung.md) | accepted | 0009 — Commit-Konventionen und rückwirkende Anonymisierung der Historie |
|
||||
| [0010](docs/adr/0010-haertung-eigener-meilenstein.md) | accepted | 0010 — Härtung ist ein eigener Meilenstein (M5); M1 misst nur Kaputtes |
|
||||
| [0011](docs/adr/0011-enrollment-localpart-kollision-verweigern.md) | accepted | 0011 — Provisionierung verweigert Localpart-Kollisionen, statt an bestehende Konten zu verknüpfen |
|
||||
| [0012](docs/adr/0012-issues-im-repo-gitlab-als-spiegel.md) | accepted | ADR-0012: Issues leben im Repo; GitLab wird deterministisch bespiegelt |
|
||||
| [0013](docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md) | accepted | ADR-0013: Gruppenregeln kanonisch im management-Repo, Komponenten zeigen und werden geprüft |
|
||||
|
||||
## Open AARs (2)
|
||||
|
||||
- [AAR — Refinement, Betrieb voranbringen, Git-Historie anonymisiert](docs/aar/2026-08-09-refinement-und-betrieb.md)
|
||||
- [AAR — `@apo` konnte nicht telefonieren: fehlende Synapse-`profiles`-Zeile](docs/aar/2026-08-11-apo-calls-profile-zeile.md)
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
# WORKFLOW.md — Gates, Sizing, and Rituals
|
||||
|
||||
Read this when a task begins, not preemptively. `AGENTS.md` holds the
|
||||
always-on rules; this file holds the process.
|
||||
|
||||
## Size Classes
|
||||
|
||||
Propose one at task start; the human confirms — individually, or batched
|
||||
at the next refinement session.
|
||||
|
||||
| Class | Scope | Process |
|
||||
|---|---|---|
|
||||
| S | One file / one small change, no design decisions | Direct. AGENTS.md rules only. The one-line go-ahead **before starting is the stop** — waived only if `PROJECT.md` grants the size-S exception. |
|
||||
| M | Few files, minor decisions, fits one session | Slice plan in chat, no file. **STOP: plan approval before any code.** Then implement; each slice reports evidence and status inline. Gate 5 is a short AAR note in chat, filed to the wiki only if it produced a real learning. |
|
||||
| L | New feature, multiple files or sessions, real decisions | Full design doc in `docs/design/` following Gates 1–5 below. |
|
||||
|
||||
When in doubt between two classes, pick the larger.
|
||||
|
||||
## Gate 0 — Project Initialization
|
||||
|
||||
Runs once per project, triggered by a missing `PROJECT.md`. Ask, never guess:
|
||||
|
||||
1. Response language? (e.g. de / en)
|
||||
2. Size-S gate exception granted? (yes / no)
|
||||
3. One-line project purpose?
|
||||
4. Audience — who uses this besides the owner? (Drives which wiki areas
|
||||
become mandatory later; see `docs/wiki/index.md`.)
|
||||
|
||||
Write the answers to `PROJECT.md` (frontmatter per `schema.yaml`), run
|
||||
`validate.py`, and confirm the result with the human.
|
||||
|
||||
## Gates 1–5 (size L)
|
||||
|
||||
Each gate is a section of the design doc. A gate ends with **STOP**:
|
||||
present the section, wait for explicit approval. Do not pre-fill later
|
||||
sections.
|
||||
|
||||
### Gate 1 — Product
|
||||
- Problem statement: what user problem, for whom.
|
||||
- Verifiable acceptance criterion. A real number where one exists;
|
||||
otherwise a concretely checkable outcome. "Works" is not a criterion.
|
||||
- Non-goals: what this deliberately does not do.
|
||||
- Announcement paragraph (3–5 sentences): what it is, who it's for, why
|
||||
it's good. If you can't write it, the product isn't understood yet.
|
||||
- UI involved? Plain-HTML mockups of the affected screens.
|
||||
|
||||
**STOP.**
|
||||
|
||||
### Gate 2 — Architecture
|
||||
- Read first: the actual codebase, relevant ADRs, relevant AARs.
|
||||
Past decisions and learnings are input, not trivia.
|
||||
- How it fits the real system: endpoints, tables/schemas, query
|
||||
outlines, the end-to-end flow (Mermaid).
|
||||
- Constraints: non-functional requirements, proportional to the project.
|
||||
- Options & trade-offs where more than one viable way exists: pro/contra
|
||||
each, chosen option, and why. Feature-local decisions stay here.
|
||||
- Lasting directional decisions discovered here become ADRs (one each),
|
||||
linked from the design doc.
|
||||
|
||||
**STOP.**
|
||||
|
||||
### Gate 3 — Program Design
|
||||
- File locations: exact paths, new and touched.
|
||||
- Types and method signatures — no bodies.
|
||||
- Call stack for the main flow(s).
|
||||
- What the tests will assert.
|
||||
- Boundaries: an explicit DO NOT CHANGE list.
|
||||
- Shakiest calls: name the decisions you are least confident about.
|
||||
|
||||
**STOP.**
|
||||
|
||||
### Gate 4 — Vertical Slices
|
||||
- Slice 1 is the tracer bullet: a thin end-to-end path that runs
|
||||
(mocks and stubs allowed). Only then real logic, one testable slice
|
||||
at a time. Never build layer-by-layer horizontally.
|
||||
- Every slice lists its tasks; every task names **files, action,
|
||||
verify, done**.
|
||||
- Each slice ends with verification evidence, a status
|
||||
(`DONE` | `DONE_WITH_CONCERNS` | `NEEDS_CONTEXT` | `BLOCKED`),
|
||||
and a **STOP** for human review before the next slice.
|
||||
|
||||
### Gate 5 — Closeout
|
||||
- AAR section in the design doc: planned / actual / why the
|
||||
difference / learnings.
|
||||
- Harvest: learnings useful to future readers go to the wiki
|
||||
(FAQ, Stolpersteine) with source links. A missing or wrong framework
|
||||
rule becomes a framework issue or update.
|
||||
- Good analyses produced along the way may be filed as wiki pages
|
||||
(with citations) instead of dying in chat history.
|
||||
- Move the design doc to `docs/design/done/`. Run `gen_status.py`.
|
||||
|
||||
## Debugging Path
|
||||
|
||||
For bugs and incidents, any size:
|
||||
|
||||
1. Reproduce first. No reproduction, no fix.
|
||||
2. Hypothesize the root cause; verify the hypothesis with evidence
|
||||
before changing anything.
|
||||
3. Route the failure before fixing (diagnostic failure routing):
|
||||
- **Intent issue** — we built toward the wrong goal → back to Gate 1.
|
||||
- **Spec issue** — the design/plan was wrong → fix the spec
|
||||
(Gate 2/3), then the code.
|
||||
- **Code issue** — plan right, code wrong → fix in place.
|
||||
4. Fix, plus a test that would have caught it.
|
||||
5. Incidents and major misdiagnoses get a standalone AAR in `docs/aar/`.
|
||||
|
||||
## Session Handoff
|
||||
|
||||
- When a slice completes, or context quality degrades, write the current
|
||||
state into the design doc's **Handoff block** — done slices, open
|
||||
decisions, next step — then start a fresh session that resumes from
|
||||
the doc. The doc is the memory; the session is disposable.
|
||||
- End every working session by answering: "Which choices did I make that
|
||||
I'm least confident about?" File the answer in the design doc.
|
||||
|
||||
## Refinement Session
|
||||
|
||||
A recurring, human-triggered ritual. Agenda:
|
||||
|
||||
1. Batched confirmations: size classes and small approvals queued since
|
||||
last time.
|
||||
2. Backlog triage over `docs/issues/`: close, reprioritize, split.
|
||||
3. AAR harvest: walk recent AARs; update the wiki (FAQ, Stolpersteine);
|
||||
propose framework changes.
|
||||
4. Wiki lint (content-level, beyond `validate.py`): contradictions
|
||||
between pages, claims superseded by newer sources, orphan pages,
|
||||
missing cross-references, gaps worth a new page or a web search.
|
||||
5. STATUS review: anything stale or surprising in `STATUS.md`.
|
||||
|
||||
## Knowledge Handling (summary)
|
||||
|
||||
Full rules live in `docs/wiki/index.md`. The short version:
|
||||
|
||||
- Original sources live in `docs/sources/`, immutable — agents read
|
||||
them, never modify them. Wiki pages cite the sources they draw on.
|
||||
- Contradictions are resolved or explicitly flagged — never left
|
||||
silently coexisting.
|
||||
- If the wiki has no confident answer, say so. Never file a
|
||||
low-confidence synthesis back as knowledge.
|
||||
- Git is the changelog. No separate log file.
|
||||
@@ -1,19 +0,0 @@
|
||||
# Architecture Decision Records (ADR)
|
||||
|
||||
Eine Datei pro Entscheidung, fortlaufend nummeriert, Format siehe
|
||||
[template.md](template.md) (MADR-light). ADRs werden **nie umgeschrieben** —
|
||||
eine revidierte Entscheidung bekommt ein neues ADR, das alte wird im Status
|
||||
auf `abgelöst durch NNNN` gesetzt.
|
||||
|
||||
**Wann ist ein ADR Pflicht:**
|
||||
|
||||
- Architektur- oder Prozessentscheidungen, die mehrere Repos/Hosts betreffen
|
||||
- **Jede dauerhafte Ausnahme von einer bestehenden Regel** — eine Ausnahme, die
|
||||
nur dokumentiert, aber nicht entschieden wurde, ist ein Fehler (gelernt beim
|
||||
git.lab-Cutover 2026-08-01: die „Übergabe-Issues bleiben auf Gitea"-Ausnahme
|
||||
hätte als Entscheidungsvorlage kommen müssen, nicht als Fußnote)
|
||||
- Verworfene Wege, deren erneute Prüfung Zeit kosten würde („warum haben wir
|
||||
das damals nicht gemacht?")
|
||||
|
||||
Kleine, repo-lokale Entscheidungen bleiben im jeweiligen Projekt (Commit-Message
|
||||
oder Issue) — nicht jede Abwägung braucht ein ADR.
|
||||
@@ -1,19 +0,0 @@
|
||||
# NNNN — Titel (Aussagesatz der Entscheidung)
|
||||
|
||||
**Status:** vorgeschlagen | akzeptiert | abgelöst durch NNNN · **Datum:** JJJJ-MM-TT · **Entscheider:** sorb
|
||||
|
||||
## Kontext
|
||||
|
||||
Was ist das Problem, was zwingt zur Entscheidung? (2–5 Sätze)
|
||||
|
||||
## Entscheidung
|
||||
|
||||
Was wurde entschieden — als klare Aussage, umsetzbar ohne den Kontext zu lesen.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
Was wird dadurch besser, was nehmen wir bewusst in Kauf, was ist jetzt Pflicht.
|
||||
|
||||
## Verworfene Alternativen
|
||||
|
||||
Je Alternative ein Satz, warum nicht.
|
||||
+8
-1
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-01
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — CVE-Pipeline `gitops#47`
|
||||
|
||||
**Datum:** 2026-08-01 · **Host/Stack:** CFGMON, `/opt/threadnet-operating/monitoring`
|
||||
@@ -51,7 +58,7 @@ Befund 2 wurde nur sichtbar, weil die Config **im Container** geprüft wurde
|
||||
aus, `up -d` meldete `Running`, und ein SIGHUP-Reload lud klaglos den alten Inhalt.
|
||||
|
||||
Diese beiden Punkte sind als Verfahren festgehalten:
|
||||
[../deploy-uebergabe.md](../deploy-uebergabe.md).
|
||||
[../deploy-uebergabe.md](../wiki/deployment/deploy-uebergabe.md).
|
||||
|
||||
## 5. Offen
|
||||
|
||||
+8
-1
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-01
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — LABNET-02, CFGMON-Seite (Übergabe `sorb/management#2`)
|
||||
|
||||
**Datum:** 2026-08-01 · **Host/Stack:** CFGMON, WireGuard-Client gegen UDM
|
||||
@@ -56,7 +63,7 @@ wurde statt der Briefing-Annahme zu folgen. `ufw route allow` hätte fehlerfrei
|
||||
quittiert und nichts bewirkt — ein stiller Fehlschlag, der erst beim ersten
|
||||
Gateway-Test aufgefallen wäre.
|
||||
|
||||
Beides sind die Punkte 1 und 2 aus [../deploy-uebergabe.md](../deploy-uebergabe.md)
|
||||
Beides sind die Punkte 1 und 2 aus [../deploy-uebergabe.md](../wiki/deployment/deploy-uebergabe.md)
|
||||
in der Praxis: Mengengerüst bzw. Verifikation dort, wo der Dienst liest.
|
||||
|
||||
## 5. Offen
|
||||
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-01
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — LABNET-02, Lab-Seite (UDM/UniFi, Einzäunung und Abnahme)
|
||||
|
||||
**Datum:** 2026-08-01 · **Host/Stack:** MorninglightMountain (UDM Pro), UniFi Policy Engine
|
||||
+8
-1
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: aar
|
||||
status: harvested
|
||||
date: 2026-08-02
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — Wiki-Rollout, Themes und Desktop-Clients (Nacht 2026-08-01/02)
|
||||
|
||||
**Datum:** 2026-08-01 22:00 – 2026-08-02 09:30 · **Beteiligt:** sorb + Mac-Session
|
||||
@@ -132,7 +139,7 @@ erfundene Palette kein Symptom, auf das man stoßen könnte.
|
||||
in den Skill-Beschreibungen **nicht verlässlich** — „Warm Sand · backgrounds"
|
||||
findet sich bei einem Theme, dessen Showcase-Seite dunkel ist. Belastbar ist nur
|
||||
`theme-showcase.pdf`: Seiten rendern, Hintergrundfarbe messen. Werte und Fallen
|
||||
stehen in [`shared/branding.md`](../../shared/branding.md).
|
||||
stehen in [`shared/branding.md`](../wiki/architecture/branding.md).
|
||||
|
||||
**Bestätigung des Musters aus Abschnitt 4.** Auch das war kein Analysefehler,
|
||||
sondern eine **ungeprüfte Änderung** — dieselbe Wurzel wie Healthcheck, toter
|
||||
+7
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: aar
|
||||
status: open
|
||||
date: 2026-08-09
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — Refinement, Betrieb voranbringen, Git-Historie anonymisiert
|
||||
|
||||
**Datum:** 2026-08-09 · **Host/Stack:** git.lab, Gitea, K3s-Cluster (Authentik,
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
type: aar
|
||||
status: open
|
||||
date: 2026-08-11
|
||||
related: []
|
||||
---
|
||||
|
||||
# AAR — `@apo` konnte nicht telefonieren: fehlende Synapse-`profiles`-Zeile
|
||||
|
||||
**Datum:** 2026-08-11 · **Beteiligt:** sorb + Mac-Session · **Stack:** Synapse,
|
||||
MAS, Authentik, Element Web / Element Call, MatrixRTC (K3s-Cluster)
|
||||
**Auftrag:** `@apo` kann sich anmelden und schreiben, aber **kein Call kommt
|
||||
zustande** — Grundursache finden und beheben, ohne weiter zu raten.
|
||||
|
||||
## 1. Ergebnis
|
||||
|
||||
**Behoben und verifiziert:**
|
||||
- `@apo` telefoniert wieder. Grundursache belegt: dem Konto fehlte die Zeile in
|
||||
Synapses `profiles`-Tabelle. Fix war ein einzelnes `INSERT` der
|
||||
Registrierungs-Default-Zeile, an zwei gesunden Konten (`clark`,
|
||||
`calltest01`) gegengeprüft.
|
||||
- Gegenprobe nach dem Fix: `displayname` gesetzt (vorher keine Zeile),
|
||||
`open_id_tokens` **0 → 6**, aktives `org.matrix.msc3401.call.member` im Raum.
|
||||
- Dokumentiert: Runbook `docs/troubleshooting/CALLS-FEHLEN-PROFILE-ZEILE.md` im
|
||||
gitops-Repo (inkl. Index-Eintrag), Merksatz im Session-Gedächtnis.
|
||||
|
||||
**Nebenbefund, separat behoben:**
|
||||
- **Kontoübernahme-Lücke:** Der MAS-Upstream-Provider stand auf
|
||||
`claims_imports.localpart.on_conflict: add` — bei Localpart-Kollision verknüpfte
|
||||
MAS die neue Upstream-Identität mit einem **bestehenden** Konto (inkl.
|
||||
Dienstkonten ohne Upstream-Link). Auf `on_conflict: fail` umgestellt
|
||||
(gitops `ef04d86`, nach git.lab gepusht), dokumentiert als
|
||||
[gitops#61](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/61),
|
||||
`priority:high`. Ausgelöst durch die live reproduzierte case-sensitive Dublette
|
||||
`boje`/`Boje`; das Zweitkonto `boje` (Authentik-ID 11) wurde gelöscht.
|
||||
**Deployment verifiziert:** Das SOPS-Values-Secret aktualisierte Flux, aber MAS
|
||||
lief noch mit der alten Config im Speicher (Pod älter als die Änderung) — erst
|
||||
ein `rollout restart` machte `fail` aktiv. „Committet" ≠ „deployed" ≠ „aktiv".
|
||||
|
||||
## 2. Die Kausalkette (belegt, nicht vermutet)
|
||||
|
||||
| Glied | Beleg |
|
||||
|---|---|
|
||||
| `@apo` hat **keine `profiles`-Zeile** | `SELECT count(*) … = 0`, während `clark`/`sorb`/`calltest01` je eine haben |
|
||||
| Displayname-Setzen crasht | `PUT …/displayname → 500`, `TypeError: 'NoneType' object is not subscriptable` in `_check_profile_size` (`storage/databases/main/profile.py:354`) — `txn.fetchone()` liefert `None`, `row[0]` fliegt |
|
||||
| kein Displayname → Widget-Init bricht ab | Call-Klick erzeugte **null** Server-Aktivität: kein `openid/request_token`, kein `call.member`; Browser-Log damals „Messaging present but not yet started" (iframe meldet nie `ContentLoaded`) |
|
||||
| kein Widget → kein Token → keine SFU | `@apo` als einziger aktiver Nutzer mit **0** Einträgen in `open_id_tokens` (die nicht geprunt werden) |
|
||||
|
||||
Herkunft der fehlenden Zeile: `@apo` ist ein **Vor-Authentik-Konto**, das durch
|
||||
sechs Identitäts-Resets ging. Deaktivieren löscht in Synapse das Profil,
|
||||
Reaktivieren legt es nicht neu an. `frank` (noch älter, nie zurückgesetzt) behielt
|
||||
seine Zeile. Ob einer der früheren manuellen Eingriffe der auslösende Reset war,
|
||||
ist nicht mehr zweifelsfrei zu klären — die Zeile ist jetzt wieder da.
|
||||
|
||||
## 3. Was ausgeschlossen wurde (gemessen)
|
||||
|
||||
| Verdacht | Warum entkräftet |
|
||||
|---|---|
|
||||
| Krypto / Cross-Signing (18 Pseudo-Geräte aus 6 Resets) | Testraum ist **unverschlüsselt** → Call braucht keine Krypto; `clark` telefoniert mit ebenfalls zurückgesetzten Schlüsseln |
|
||||
| Server-Call-Pfad (SFU, RTC-Auth, OpenID-Endpoint) | `calltest01`/`sorb` bekommen sauber 200 auf `openid/request_token` und die Federation-Auflösung |
|
||||
| `@apo`s Token / Session | `/sync` läuft durchgehend mit 200, Messaging intakt |
|
||||
| Login-Verknüpfung MAS↔Authentik | `subject` = Authentik-`uid` `2fafe38b…`, korrekt |
|
||||
|
||||
## 4. Was zur Lösung geführt hat
|
||||
|
||||
- **Der Sprung von „welcher Nutzer telefoniert nicht" zu „welche *Tabelle* ist
|
||||
anders".** Der Durchbruch war die `open_id_tokens`-Abfrage über *alle* aktiven
|
||||
Nutzer: `@apo` = 0, alle anderen zweistellig+. Ein Vergleich statt einer
|
||||
Einzelbetrachtung.
|
||||
- **Ein unverschlüsselter Testraum** hat das größte Ablenkungsfeld
|
||||
(Cross-Signing) in einem Schritt geschlossen.
|
||||
- **Der Live-Mitschnitt beim echten Call-Klick** zeigte die Abwesenheit jeder
|
||||
Aktivität — nicht ein Fehler, sondern *nichts* war der Befund.
|
||||
- **Der Nutzer-Hinweis „Anzeigename konnte nicht gesetzt werden"** lieferte den
|
||||
500er mit vollständigem Stacktrace — die letzte Meile von Korrelation zu
|
||||
Ursache.
|
||||
- **Der entscheidende Kontext kam von sorb:** „`apo` ist ein Alt-Konto von vor
|
||||
der Authentik-Integration." Das lenkte die Suche von „angesammelter Müll" auf
|
||||
„Migrations-/Provisionierungs-Lücke".
|
||||
|
||||
## 5. Lehren für die Zukunft
|
||||
|
||||
1. **Bei Call-Problemen zuerst `open_id_tokens` je Nutzer vergleichen.** 0 bei
|
||||
einem sonst aktiven Konto ist das schnellste, eindeutigste Alarmsignal und
|
||||
trennt Client- von Server-Ursache in einer Abfrage.
|
||||
2. **Immer im unverschlüsselten Raum reproduzieren, bevor man Krypto verdächtigt.**
|
||||
Das schließt einen ganzen Ursachenblock kostenlos aus.
|
||||
3. **„Nichts passiert" ist ein Messergebnis, kein Sackgassen-Signal.** Die
|
||||
Abwesenheit eines `openid`-Aufrufs hat den Fehler lokalisiert, nicht ein
|
||||
Fehlercode.
|
||||
4. **Alt-/mehrfach-zurückgesetzte Konten gegen frisch provisionierte diffen,
|
||||
nicht nur gegen die Erwartung.** Der Unterschied war eine *fehlende* Zeile —
|
||||
sichtbar nur im direkten Vergleich mit `clark`/`calltest01`.
|
||||
5. **Jeder DB-Schreib strukturiert: betroffene Zeile vorher anzeigen, an einem
|
||||
gesunden Konto gegenprüfen, per `INSERT … ON CONFLICT DO NOTHING` statt
|
||||
Überschreiben.** Das ist die direkte Konsequenz aus den früheren
|
||||
unstrukturierten MAS-Eingriffen dieses Vorgangs — und diesmal eingehalten.
|
||||
6. **Beiläufige Symptome ernst nehmen:** die Dublette `boje`/`Boje` beim
|
||||
Testkonto-Anlegen war der Faden, der die Kontoübernahme-Lücke (gitops#61)
|
||||
aufdeckte — ein Sicherheitsfund, der ohne den `@apo`-Vorgang unentdeckt
|
||||
geblieben wäre.
|
||||
|
||||
## 6. Offen / Folgetodos
|
||||
|
||||
- **gitops#61** (`on_conflict`-Härtung) ist gepusht und rollt über Flux; der
|
||||
case-insensitive Eindeutigkeits-Check im `matrix-invitation`-Prompt-Stage
|
||||
(damit der Nutzer schon bei der Registrierung statt erst beim Login scheitert)
|
||||
ist dort als bewusst offener Rest vermerkt.
|
||||
- Verwaiste Altlasten bei `@apo` (10 `local_notification_settings` für längst
|
||||
gelöschte Geräte, 18 Cross-Signing-Pseudoeinträge) sind **kosmetisch** und
|
||||
wurden bewusst **nicht** angefasst — sie haben mit dem Call-Problem nichts zu
|
||||
tun, und ein weiterer Eingriff widerspräche der Lehre oben.
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
type: aar
|
||||
status: open # open | harvested
|
||||
date: YYYY-MM-DD
|
||||
related: [] # design docs, issues, ADRs involved
|
||||
---
|
||||
|
||||
<!-- Copy to docs/aar/YYYY-MM-DD-slug.md. Delete comments when filling in.
|
||||
Standalone AARs are for incidents and major deviations only —
|
||||
normal undertakings get their AAR as Gate 5 inside the design doc. -->
|
||||
|
||||
# AAR: Title
|
||||
|
||||
## What was planned / expected
|
||||
|
||||
## What happened
|
||||
|
||||
<!-- Facts and timeline, not blame. -->
|
||||
|
||||
## Why the difference
|
||||
|
||||
<!-- Root cause. For failures, name the routing class:
|
||||
intent issue / spec issue / code issue. -->
|
||||
|
||||
## Learnings
|
||||
|
||||
<!-- What future-you should know. Blunt beats polite. -->
|
||||
|
||||
## Actions
|
||||
|
||||
<!-- Concrete: wiki pages updated (FAQ, Stolpersteine) with links,
|
||||
framework issues opened, tests added. When all actions are done,
|
||||
set status: harvested. The refinement session walks all AARs
|
||||
still marked open. -->
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0001"
|
||||
status: accepted
|
||||
date: 2026-07-31
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0001 — git.lab ist kanonisch, Gitea wird per Push-Mirror beliefert
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-07-31 (rückwirkend dokumentiert 2026-08-01) · **Entscheider:** sorb
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0002"
|
||||
status: accepted
|
||||
date: 2026-08-01
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0002 — Issues und Management-Repo ziehen ins Lab („das Lab ist die Quelle der Wahrheit")
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-01 · **Entscheider:** sorb
|
||||
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0003"
|
||||
status: accepted
|
||||
date: 2026-08-01
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0003 — CVE-Meldeweg: aggregierte Alarme, eigener Security-Raum, gleicher Bot
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-01 · **Entscheider:** sorb
|
||||
+11
-1
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0004"
|
||||
status: accepted
|
||||
date: 2026-08-01
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0004 — Site-to-Site-VPN Hetzner-Projektnetz ↔ Lab, schaltbar über die UDM
|
||||
|
||||
**Status:** akzeptiert (umgesetzt und abgenommen 2026-08-01, Testreihe 1–7 in [management#12](https://git.lab/axion1337.chat/management/-/issues/12)) · **Datum:** 2026-08-01 · **Entscheider:** sorb
|
||||
@@ -63,4 +73,4 @@ Client". Die Richtung wurde deshalb gedreht:
|
||||
- Tunnel dauerhaft an: widerspricht dem Bedarfsfall-Prinzip ohne echten Gewinn.
|
||||
- git.lab öffentlich exponieren: größte Angriffsfläche, klar verworfen.
|
||||
- Eigene UniFi-Zone für den Tunnel: technisch nicht möglich (VPN-Server bleiben in der
|
||||
VPN-Zone), siehe [AAR Lab-Seite](../verfahren/aar/2026-08-01-labnet02-lab.md).
|
||||
VPN-Zone), siehe [AAR Lab-Seite](../aar/2026-08-01-labnet02-lab.md).
|
||||
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0005"
|
||||
status: accepted
|
||||
date: 2026-08-01
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0005 — Projektmanagement: Kanban-Rückgrat mit leichten Scrum-Elementen
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-01 · **Entscheider:** sorb
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0006"
|
||||
status: accepted
|
||||
date: 2026-08-02
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0006 — Wikis ins Lab konsolidieren, Docusaurus als gemeinsame Lesefläche
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-02 · **Entscheider:** sorb
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0007"
|
||||
status: proposed
|
||||
date: 2026-08-02
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0007 — Wiki-Oberfläche: Docusaurus läuft, BookStack als Gegenentwurf
|
||||
|
||||
**Status:** vorgeschlagen (Entscheidung offen → [Issue #20](https://git.lab/axion1337.chat/management/-/issues/20)) · **Datum:** 2026-08-02 · **Entscheider:** sorb
|
||||
+11
-1
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0008"
|
||||
status: accepted
|
||||
date: 2026-08-06
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0008 — Agenten-Sessions auf CFGMON laufen root-äquivalent über die docker-Gruppe
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-06 (Struktur-Workshop [#17](https://git.lab/axion1337.chat/management/-/issues/17)) · **Entscheider:** sorb
|
||||
@@ -19,7 +29,7 @@ Konfigurationsfehler — aber es hat zwei Folgen, die benannt gehören:
|
||||
docker-Gruppe geschieht, ist im Nachhinein nicht aus den üblichen
|
||||
Protokollen rekonstruierbar.
|
||||
|
||||
Aufgedeckt im [CFGMON-AAR](../verfahren/aar/2026-08-01-labnet02-cfgmon.md)
|
||||
Aufgedeckt im [CFGMON-AAR](../aar/2026-08-01-labnet02-cfgmon.md)
|
||||
(Befund 3, MEDIUM), erfasst als
|
||||
[#14](https://git.lab/axion1337.chat/management/-/issues/14).
|
||||
|
||||
+12
-2
@@ -1,8 +1,18 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0009"
|
||||
status: accepted
|
||||
date: 2026-08-07
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0009 — Commit-Konventionen und rückwirkende Anonymisierung der Historie
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-07 (Regel) / 2026-08-09 (Durchführung) · **Entscheider:** sorb
|
||||
|
||||
> Nachgetragen am 2026-08-09 in der [Retro](../verfahren/retro/2026-08-09.md). Die
|
||||
> Nachgetragen am 2026-08-09 in der [Retro](../sources/protokolle/retro-2026-08-09.md). Die
|
||||
> Entscheidung war getroffen und ausgeführt, bevor sie als ADR vorlag — das ist
|
||||
> genau der Fehler, den die ADR-Pflicht verhindern soll, und wird hier benannt
|
||||
> statt geglättet.
|
||||
@@ -51,7 +61,7 @@ Uhrzeit verschwindet.
|
||||
- **Alle SHAs im Bereich sind neu.** Verweise in Issues, Doku und Commit-Texten
|
||||
zeigen ins Leere. Die Doku wurde nachgezogen (12 Stellen); für alles andere gibt
|
||||
es die dauerhafte Zuordnungstabelle
|
||||
[`shared/commit-zuordnung-2026-08-07.md`](../shared/commit-zuordnung-2026-08-07.md).
|
||||
[`shared/commit-zuordnung-2026-08-07.md`](../sources/migration/commit-zuordnung-2026-08-07.md).
|
||||
- **Issue-Kommentare wurden bewusst NICHT umgeschrieben.** Eine Tabelle
|
||||
nachzuschlagen ist zumutbar; nachträglich zu ändern, was jemand geschrieben hat,
|
||||
beschädigt dieselbe Nachvollziehbarkeit ein zweites Mal.
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0010"
|
||||
status: accepted
|
||||
date: 2026-08-09
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0010 — Härtung ist ein eigener Meilenstein (M5); M1 misst nur Kaputtes
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-09 · **Entscheider:** sorb
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0011"
|
||||
status: accepted
|
||||
date: 2026-08-11
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related: []
|
||||
---
|
||||
|
||||
# 0011 — Provisionierung verweigert Localpart-Kollisionen, statt an bestehende Konten zu verknüpfen
|
||||
|
||||
**Status:** akzeptiert · **Datum:** 2026-08-11 · **Entscheider:** sorb
|
||||
|
||||
## Kontext
|
||||
|
||||
Der MAS-Upstream-Provider für Authentik stand auf
|
||||
`claims_imports.localpart.on_conflict: add`. MAS-Semantik: Kollidiert der aus dem
|
||||
Authentik-Claim abgeleitete Localpart mit einem **bestehenden** Matrix-Konto,
|
||||
verknüpft MAS die neue Upstream-Identität mit diesem Konto — ohne Abbruch, ohne
|
||||
Warnung. Authentiks eigene Benutzernamen-Eindeutigkeit fängt das nicht ab: sie
|
||||
gilt nur innerhalb von Authentik und ist case-sensitive (`boje` neben `Boje` ging
|
||||
live durch). Folge: Ein Inhaber eines Einladungstokens konnte einen (auch nur in
|
||||
der Schreibweise abweichenden) Namen eines bestehenden Kontos registrieren und
|
||||
würde beim ersten Login in dessen Konto verknüpft — inklusive Dienstkonten ohne
|
||||
Upstream-Link (`draupnir`, `alerts`, `maintenance-notify`). Das ist ein
|
||||
Kontoübernahme-Vektor, entdeckt am 2026-08-11 beim Anlegen eines Testkontos
|
||||
([gitops#61](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/61)).
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Identitäts-Provisionierung verknüpft eine neue Upstream-Identität niemals mit
|
||||
einem bereits bestehenden lokalen Konto.** Konkret: `on_conflict: fail` im
|
||||
`claims_imports.localpart`-Block des MAS-Upstream-Providers
|
||||
(`gitops/apps/production/custom-configs/mas-secret.yaml`). Ein kollidierender
|
||||
Localpart bricht die Provisionierung ab. Dies ist ab jetzt stehende Regel, nicht
|
||||
nur der aktuelle Wert — jede künftige Änderung an diesem Verhalten braucht ein
|
||||
ablösendes ADR.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- **Besser:** Der Übernahme-Weg ist geschlossen. Bestehende Konten (besonders die
|
||||
ohne Upstream-Link) können nicht mehr durch eine kollidierende Neuregistrierung
|
||||
gekapert werden. Bestehende, korrekte Verknüpfungen bleiben unberührt.
|
||||
- **In Kauf genommen:** Ein Nutzer, der einen bereits vergebenen Namen wählt,
|
||||
erhält die Fehlermeldung erst **beim Login** (wenn MAS provisioniert), nicht
|
||||
schon bei der Registrierung in Authentik. Das ist eine schlechtere UX, aber kein
|
||||
Sicherheitsproblem.
|
||||
- **Jetzt Pflicht:**
|
||||
- Als offene Härtung eine **case-insensitive Eindeutigkeitsprüfung im
|
||||
`matrix-invitation`-Prompt-Stage**, damit die Kollision schon bei der
|
||||
Registrierung sichtbar wird (verfolgt in gitops#61).
|
||||
- **Nach jeder Änderung an einem SOPS-verwalteten Values-Secret den
|
||||
konsumierenden Dienst per `rollout restart` neu ausrollen und verifizieren**,
|
||||
dass der Pod jünger als die Änderung ist. Beim Ausrollen dieses Fixes lief
|
||||
MAS noch mit der alten Config im Speicher, obwohl das Secret bereits `fail`
|
||||
zeigte — „committet" ≠ „deployed" ≠ „aktiv" (MAS liest Config nur beim Start).
|
||||
|
||||
## Verworfene Alternativen
|
||||
|
||||
- **`on_conflict: add` belassen und allein auf Authentiks Eindeutigkeit
|
||||
vertrauen** — verworfen: die greift nur innerhalb Authentiks und
|
||||
case-sensitive, deckt Kollisionen mit vorbestehenden Matrix-Konten also nicht ab.
|
||||
- **Nur den Prompt-Stage-Check bauen, MAS auf `add` lassen** — verworfen: der
|
||||
Client-seitige Check ist umgehbar (direkter Flow-Aufruf), der MAS-seitige
|
||||
Abbruch ist die eigentliche Sicherheitsgrenze. Der Prompt-Check ist die
|
||||
UX-Ergänzung, nicht der Schutz.
|
||||
- **Betroffene Dienstkonten einfach mit Upstream-Links versehen** — verworfen:
|
||||
behandelt nur das Symptom für heute bekannte Konten, nicht den Mechanismus.
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0012"
|
||||
status: accepted
|
||||
date: 2026-08-11
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/design/done/2026-08-11-neckbeard-migration.md"
|
||||
- "docs/adr/0002-issues-und-management-ins-lab.md"
|
||||
- "docs/adr/0005-pm-framework-kanban.md"
|
||||
---
|
||||
|
||||
# ADR-0012: Issues leben im Repo; GitLab wird deterministisch bespiegelt
|
||||
|
||||
## Kontext
|
||||
|
||||
Die Gruppe führt 111 Issues auf git.lab, davon 71 offen; die Disziplin ist
|
||||
belegt intakt (F-014: 71/71 mit genau einem Meilenstein, 71/71 mit
|
||||
Priorität, WIP-Limit gehalten). Neckbeards ADR-0002 macht In-Repo-Issues
|
||||
zum Default und vertagt die Spiegel-Option C. Der Feldtest zeigt beides:
|
||||
Die Forge erzwingt sichtbar, was Prosa nicht hält (F-001, F-017 —
|
||||
Dokumente widersprechen dem Board), und Host-Sessions ohne Lab-Zugang
|
||||
können GitLab-Issues gar nicht lesen, wohl aber den Gitea-Mirror dieses
|
||||
Repos. Der alte Grundsatz „Alles Offene ist ein Issue" (altes ADR-0005)
|
||||
scheiterte nur dort, wo Arbeitspunkte in `hosts/`-Markdown lebten (F-004)
|
||||
— am zweiten Backlog, nicht am Board.
|
||||
|
||||
## Optionen
|
||||
|
||||
**A: GitLab bleibt kanonisch, Repo hält nur einen Export.** Tagesablauf
|
||||
unverändert, Board bleibt Arbeitsfläche. Aber: dauerhafte Ausnahme von
|
||||
neckbeards ADR-0002 (nach eigener Regel ADR-pflichtig), Issues bleiben
|
||||
für Host-Sessions unsichtbar und für Agenten nur per API erreichbar, und
|
||||
die Klasse „Prosa widerspricht Board" (F-001) bleibt strukturell offen —
|
||||
generierte Dokumente hingen an einem Netzzugriff.
|
||||
|
||||
**B: Reine In-Repo-Issues, GitLab-Issues geschlossen.** Sauberste
|
||||
neckbeard-Form. Aber: das Gruppenboard verliert den Management-Scope,
|
||||
Meilenstein-Ansichten werden unvollständig, das Refinement liest zwei
|
||||
Systeme — genau die belegte Disziplin (F-014) würde ihres Werkzeugs
|
||||
beraubt. Der Report warnt ausdrücklich: nicht per Board-Löschung
|
||||
migrieren.
|
||||
|
||||
**C: Repo kanonisch, GitLab als generierter Spiegel.** Die Issue-Wahrheit
|
||||
liegt als `docs/issues/NNNN-slug.md` im Repo (grepbar, offline, über den
|
||||
Gitea-Mirror überall lesbar); ein deterministisches Skript spiegelt
|
||||
Titel, Status, Meilenstein, Priorität und Fälligkeit nach GitLab, damit
|
||||
Board-, Meilenstein- und Label-Ansichten weiterarbeiten. Eine
|
||||
Drift-Prüfung meldet Abweichungen zwischen Board und Repo rot.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Option C, beschränkt auf den Management-Scope.**
|
||||
|
||||
- `docs/issues/` wird kanonisch für die Issues des management-Projekts.
|
||||
Die offenen management-Issues werden aus dem GitLab-Stand importiert
|
||||
und behalten ihre Nummern (GitLab-iid = Datei-id; keine dritte
|
||||
Nummernwelt). Alt-IDs wie `CFGMON-01` bleiben im Titel.
|
||||
- Das Schema trägt die belegten Pflichten: `milestone` (Pflicht, M1–M5)
|
||||
und `priority` (Pflicht, high/medium/low), dazu `due` (Datum statt
|
||||
„bald"), optional `host`/`area`. Der Status-Enum wird um die
|
||||
Board-Spalten erweitert (`next`, `waiting` mit benanntem Grund); das
|
||||
WIP-Limit (max. 2 in-progress) wird eine Validator-Regel.
|
||||
- Der Spiegel ist **ein** deterministisches Skript (Repo → GitLab),
|
||||
Standard `--dry-run`; echte Läufe stößt sorb an. Board-Handgriffe
|
||||
bleiben erlaubt, sind aber nicht kanonisch: Was nicht nachgezogen
|
||||
wird, meldet die Drift-Prüfung. Die Zusage-Spalten (`next`,
|
||||
`in-progress`) vergibt weiterhin nur sorb — Prozessregel, nicht
|
||||
Mechanik.
|
||||
- **Komponenten-Tracker bleiben unangetastet** (gitops 60 Issues usw.),
|
||||
bis die jeweilige Komponente selbst adoptiert; das wird als
|
||||
Folge-Issues angelegt. Bis dahin gilt für Komponenten-Issues GitLab
|
||||
als Wahrheit — ausgewiesen, nicht verschwiegen.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- Statusänderung = Commit; `git log` ersetzt die Issue-Chronik. STATUS.md
|
||||
und Roadmap-Zahlen werden generiert statt behauptet (F-001-Klasse
|
||||
geschlossen).
|
||||
- Host-Sessions lesen den vollständigen Management-Backlog erstmals von
|
||||
überall (Gitea-Mirror des Repos).
|
||||
- GitLab-seitige Änderungen ohne Nachzug sind ab jetzt ein Befund, kein
|
||||
stiller Zustand — die Drift-Prüfung übernimmt die Alarmfunktion der
|
||||
roten Pipeline.
|
||||
- Das geschlossene GitLab-Altbestand-Archiv (40 geschlossene Issues)
|
||||
wird nicht importiert; es bleibt als Historie auf git.lab, erreichbar
|
||||
über die bestehenden Verweise.
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0013"
|
||||
status: accepted
|
||||
date: 2026-08-11
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
related:
|
||||
- "docs/design/done/2026-08-11-neckbeard-migration.md"
|
||||
- "docs/adr/0001-gitlab-kanonisch-push-mirror.md"
|
||||
---
|
||||
|
||||
# ADR-0013: Gruppenregeln kanonisch im management-Repo, Komponenten zeigen und werden geprüft
|
||||
|
||||
## Kontext
|
||||
|
||||
Fünf Komponenten-Repos und dieses Repo teilen ein Regelwerk. Neckbeards
|
||||
ADR-0001 löst „ein Repo, viele Harnesse", nicht „viele Repos, ein
|
||||
Regelwerk" — die schärfste Lücke des Feldtests. Der alte Ansatz war
|
||||
bereits Pointer-basiert („Projekt-Repos haben eigene CLAUDE.mds", die
|
||||
Arbeitsgrundlage liegt im management-Repo, über den Gitea-Mirror von
|
||||
überall lesbar) und scheiterte nicht am Mechanismus, sondern an der
|
||||
Anwendung: 4 von 5 Komponenten haben schlicht keine Pointer-Datei
|
||||
(F-011), und nichts prüfte das. Zusätzlich tragen fünf Komponenten vier
|
||||
Namensschemata, ohne dass ein Artefakt den kanonischen Slug festhält
|
||||
(F-008) — diese Session musste die Slugs erfragen.
|
||||
|
||||
## Optionen
|
||||
|
||||
**A: Regelkopien in jede Komponente stempeln** (generiert, mit
|
||||
Quell-SHA; Prüfskript vergleicht). Funktioniert offline im
|
||||
Komponenten-Checkout. Aber: sechs Kopien derselben Regeln sind genau die
|
||||
Drift-Maschine, die ADR-0001 upstream verwirft — der Stempel macht Drift
|
||||
erkennbar, nicht unmöglich, und jeder Regeländerung folgt ein
|
||||
Sechs-Repo-Commit-Zug.
|
||||
|
||||
**B: Git-Submodule/Subtree eines Regel-Repos.** Mechanisch streng, aber:
|
||||
koppelt jeden Komponenten-Clone an Lab-Erreichbarkeit, ist in Obsidian
|
||||
und Forge-Ansichten sperrig, und die Gruppe hat mit Submodules keinerlei
|
||||
Praxis — Reibung ohne belegten Bedarf.
|
||||
|
||||
**C: Pointer + deterministische Prüfung.** Die Gruppenregeln stehen
|
||||
genau einmal, im AGENTS.md dieses Repos (das gespiegelt und damit
|
||||
überall lesbar ist). Jede Komponente trägt nur Projektspezifika plus
|
||||
einen Pointer auf die Gruppenregeln (git.lab-Pfad und Mirror-URL). Neu
|
||||
gegenüber dem alten Ansatz ist der prüfende Teil: ein Artefakt benennt
|
||||
die Gruppe, ein Skript prüft die Anwendung.
|
||||
|
||||
## Entscheidung
|
||||
|
||||
**Option C.**
|
||||
|
||||
- **Kanonisch:** die Gruppenregeln leben als ausgewiesener Abschnitt im
|
||||
`AGENTS.md` dieses Repos. `CLAUDE.md` wird Ein-Zeilen-Pointer
|
||||
(neckbeard ADR-0001).
|
||||
- **Komponenten-Artefakt:** `docs/components/<slug>.md` (neuer
|
||||
Schema-Typ) deklariert je Repo den kanonischen Slug, Anzeigenamen,
|
||||
Mirror-Pfad und die Phase (`active` / `staged` / `external`) — damit
|
||||
ist F-008 maschinenlesbar beantwortet und die bewusst gestaffelte
|
||||
Dormanz von `thread-net-git`/`threadnet-operating` (F-009-Addendum)
|
||||
erstmals repräsentierbar statt nur mündlich.
|
||||
- **Prüfung, zweigeteilt:** offline prüft `validate.py` die
|
||||
Komponenten-Artefakte wie jedes andere Artefakt; in der Lab-CI prüft
|
||||
die Stillstandsprüfungs-Familie (a) dass jede deklarierte Komponente
|
||||
die Pointer-Datei tatsächlich trägt (schließt F-011) und (b) dass die
|
||||
**zur Laufzeit gelesene** Gruppenliste und `docs/components/`
|
||||
deckungsgleich sind — die Projektliste bleibt bewusst ungehärtet im
|
||||
Code (Retro-Lehre: eine gepflegte Liste ist die Stelle, an der ein
|
||||
neues Repo jahrelang durchrutscht); neu auftauchende Repos werden
|
||||
Befund statt Lücke.
|
||||
- **Rollout** der Pointer-Dateien in die fünf Komponenten ist nicht Teil
|
||||
dieser Undertaking: fünf Folge-Issues, eines je Komponente.
|
||||
|
||||
## Konsequenzen
|
||||
|
||||
- Regeländerung = ein Commit in einem Repo; Komponenten folgen per
|
||||
Verweis, nicht per Kopie.
|
||||
- Eine Komponente ohne Pointer ist ab dem Rollout ein roter
|
||||
CI-Befund, kein stiller Zustand über Wochen (F-011-Klasse).
|
||||
- Die Slug-Unregelmäßigkeiten selbst (`thread-net-git`,
|
||||
CamelCase-`ThreadNet-Web`) werden hier **nicht** bereinigt — ein
|
||||
Rename fasst Forge-Zustand an und wird eigenes Issue mit eigener
|
||||
Abwägung; das Artefakt dokumentiert bis dahin den Ist-Stand.
|
||||
- Host-Sessions ohne Lab finden Regeln und Gruppenliste über den
|
||||
Gitea-Mirror; der Pointer nennt beide Wege.
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0000"
|
||||
status: proposed # proposed | accepted | superseded
|
||||
date: YYYY-MM-DD
|
||||
supersedes: null # path to older ADR, e.g. docs/adr/0002-old.md
|
||||
superseded_by: null # filled in on the OLD adr when a new one replaces it
|
||||
related: [] # optional: paths to design docs / issues
|
||||
---
|
||||
|
||||
<!-- Copy to docs/adr/NNNN-slug.md. Delete all comments when filling in. -->
|
||||
|
||||
# ADR-0000: Title
|
||||
|
||||
## Context
|
||||
|
||||
<!-- The situation and the forces at play. Constraints upfront:
|
||||
deadlines, scale, team knowledge, existing decisions. -->
|
||||
|
||||
## Options Considered
|
||||
|
||||
<!-- Name each option, even the one you lean toward. Pros/cons per
|
||||
option; a small dimension table (complexity, cost, maintenance,
|
||||
familiarity) where it helps. Keep proportional to the decision. -->
|
||||
|
||||
## Decision
|
||||
|
||||
<!-- The choice, in one or two sentences. -->
|
||||
|
||||
## Consequences
|
||||
|
||||
<!-- What becomes easier, what becomes harder, what we will need to
|
||||
revisit. Honest cons included. -->
|
||||
|
||||
<!-- Rules: an accepted ADR is never edited — write a new ADR that
|
||||
supersedes it and set superseded_by here. Lasting directional
|
||||
decisions only; feature-local choices belong in the design doc. -->
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "ThreadNet-Web"
|
||||
anzeigename: "ThreadNet Web"
|
||||
phase: active
|
||||
gitlab: "axion1337.chat/ThreadNet-Web"
|
||||
mirror: "rohana.axion1337.de/sorb/ThreadNet-Web"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# ThreadNet Web
|
||||
|
||||
Element-Web/-Desktop-Fork unter eigener Marke. ⚠️ Slug ist der einzige in CamelCase (F-008) — GitLab behandelt Pfade case-insensitiv; kanonisch ist exakt diese Schreibweise. Ein Rename ist bewusst NICHT Teil der Migration (eigenes Issue bei Bedarf, ADR-0013).
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "axion1337.chat-gitops"
|
||||
anzeigename: "ThreadNet Server Suite"
|
||||
phase: active
|
||||
gitlab: "axion1337.chat/axion1337.chat-gitops"
|
||||
mirror: "rohana.axion1337.de/sorb/axion1337.chat-gitops"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# ThreadNet Server Suite
|
||||
|
||||
ESS-/Flux-Deployment des axion1337.chat-Stacks; Gitea bleibt Flux-Quelle ([Mirror-Topologie](../wiki/architecture/mirror-topologie.md)). Trägt die Hälfte des Gruppen-Backlogs (Feldtest F-009).
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "game-operating"
|
||||
anzeigename: "Game-Operating"
|
||||
phase: external
|
||||
gitlab: "axion1337.chat/game-operating"
|
||||
mirror: "rohana.axion1337.de/sorb/game-operating" # privat — anonym nicht lesbar (F-007-Addendum)
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Game-Operating
|
||||
|
||||
Nicht ThreadNet-bezogen (über geplante Monitoring-Aufnahme hinaus). Mirror existiert **privat** — ein anonymer ls-remote-Fehlschlag ist hier kein Beleg für Nichtexistenz (Feldtest-Lehre, F-007-Addendum).
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "gameserver"
|
||||
anzeigename: "Gameserver"
|
||||
phase: external
|
||||
gitlab: "axion1337.chat/gameserver"
|
||||
mirror: null # kein Mirror — Issue 0032
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Gameserver
|
||||
|
||||
Nicht ThreadNet-bezogen. **Kein Push-Mirror**; auf Gitea liegt ein gleichnamiges Repo mit anderem Stand — verfolgt in [Issue 0032](../issues/0032-gameserver-hat-keinen-push-mirror-und-auf-gitea.md).
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "management"
|
||||
anzeigename: "Management"
|
||||
phase: active
|
||||
gitlab: "axion1337.chat/management"
|
||||
mirror: "rohana.axion1337.de/sorb/management"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Management
|
||||
|
||||
Dieses Repo: Steuerung der Gruppe, kanonische Gruppenregeln (AGENTS.md §6), In-Repo-Issues (ADR-0012).
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "thread-net-git"
|
||||
anzeigename: "ThreadNet Git"
|
||||
phase: staged
|
||||
gitlab: "axion1337.chat/thread-net-git"
|
||||
mirror: "rohana.axion1337.de/sorb/thread-net-git"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# ThreadNet Git
|
||||
|
||||
Gitea-Betriebskonfiguration. **Bewusst gestaffelt dormant** (sorb, 2026-08-10, Feldtest F-009-Addendum): erst Basis-Funktionsumfang, dann Monitoring-/Security-Ausbau — keine Politur-Umwege. ⚠️ Slug bricht das threadnet-Muster (F-008); Ist-Stand dokumentiert, kein Rename hier.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "threadnet-call"
|
||||
anzeigename: "ThreadNet Call"
|
||||
phase: active
|
||||
gitlab: "axion1337.chat/threadnet-call"
|
||||
mirror: "rohana.axion1337.de/sorb/threadnet-call"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# ThreadNet Call
|
||||
|
||||
Element-Call-Fork für ThreadNet.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
type: component
|
||||
slug: "threadnet-operating"
|
||||
anzeigename: "ThreadNet Operating"
|
||||
phase: staged
|
||||
gitlab: "axion1337.chat/threadnet-operating"
|
||||
mirror: "rohana.axion1337.de/sorb/threadnet-operating"
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# ThreadNet Operating
|
||||
|
||||
CFGMON-Betrieb (Monitoring/Konfiguration). **Bewusst gestaffelt dormant** wie thread-net-git (F-009-Addendum).
|
||||
@@ -0,0 +1,642 @@
|
||||
---
|
||||
type: design
|
||||
status: done
|
||||
date: 2026-08-11
|
||||
size: L
|
||||
related:
|
||||
- "PROJECT.md"
|
||||
- "docs/adr/0005-pm-framework-kanban.md"
|
||||
- "docs/adr/0009-commit-konventionen-und-historien-anonymisierung.md"
|
||||
- "docs/adr/0010-haertung-eigener-meilenstein.md"
|
||||
- "docs/adr/0012-issues-im-repo-gitlab-als-spiegel.md"
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Design: Migration des Management-Systems auf neckbeard
|
||||
|
||||
Grundlage: der Feldtest-Report auf dem Branch `Neckbeard-v0.1.1-analyse-1`
|
||||
(Session 1, eingefroren; Befunde F-001…F-017), gemessen gegen neckbeard
|
||||
v0.1.1 @ `823a08cac6b03a47d7e2f661200a49ac6e09d38d`. Bindende Vorgabe aus
|
||||
der Übergabe: **erst die Fehlermuster beider Ansätze durcharbeiten und den
|
||||
Wert des alten Ansatzes in neckbeard einfalten — Übernahme erst danach.**
|
||||
|
||||
## Gate 1 — Produkt
|
||||
|
||||
### Problem
|
||||
|
||||
Das Management-Repo steuert fünf Komponenten-Repos und sich selbst mit
|
||||
einem eigenen Regelwerk. Der Feldtest zeigt: Das Regelwerk ist nicht
|
||||
verfallen, sondern **ungleich durchgesetzt**. Wo ein Werkzeug die Regel
|
||||
hält, hält sie vollständig — alle 71 offenen Issues haben genau einen
|
||||
Meilenstein, das WIP-Limit steht, 0 von 161 Dokument-Links sind tot
|
||||
(F-014). Wo nichts prüft — Prosa, Git-Metadaten, Übereinstimmung zweier
|
||||
Dateien — versagt dasselbe Regelwerk wiederholt, in vier Mustern:
|
||||
|
||||
- **A** — Entscheidung im Werkzeug vollzogen, Doku nicht nachgezogen:
|
||||
M5 existiert und trägt 14 Issues, aber `roadmap.md` stellt ihn als
|
||||
offene Frage dar und die kanonische Arbeitsgrundlage bindet Issues an
|
||||
„M1–M4" (F-001; ferner F-005, F-007, F-010, F-017).
|
||||
- **B** — Regel repo-weit erklärt, auf eine Teilmenge angewandt:
|
||||
Zeitstempel-Anonymisierung erreicht 1 von 6 Repos, 5 Autor-Identitäten
|
||||
einer Person überleben, 4 von 5 Komponenten haben kein versprochenes
|
||||
CLAUDE.md, fünf Komponenten tragen vier Namensschemata (F-002, F-003,
|
||||
F-008, F-011).
|
||||
- **C** — zwei Backlogs, eine Regel: fünf offene Arbeitspunkte leben nur
|
||||
in `hosts/`-Markdown, unsichtbar für Board, Meilenstein und Priorität
|
||||
(F-004, F-009).
|
||||
- **D** — Artefakte überleben ihren Zweck ohne Eigentümer: verwaiste
|
||||
Branches publizieren Vor-Rewrite-Historie, zitierte SHAs sind
|
||||
unauflösbar (F-006, F-012).
|
||||
|
||||
Betroffen sind sorb und jede Agenten-Session: Jede neue Session wird von
|
||||
der kanonischen Datei falsch geprimt und würde vollzogene Entscheidungen
|
||||
rückgängig machen. Neckbeard adressiert genau diese Klasse — hält aber
|
||||
selbst sieben im Feldtest belegte Lücken, allen voran: ADR-0001 löst
|
||||
„ein Repo, viele Harnesse", dieses Projekt ist „viele Repos, ein
|
||||
Regelwerk", und für die Frage, wo die 71 offenen GitLab-Issues nach der
|
||||
Migration leben, existiert nur eine aufgeschobene Option C. Beide
|
||||
Entscheidungen fallen in Gate 2, jeweils als ADR.
|
||||
|
||||
Das Produkt dieser Undertaking: das Management-System dieses Repos auf
|
||||
neckbeard umziehen, so dass die vorhandene Disziplin von Stellen, die
|
||||
nur ein Mensch prüfen kann, an Stellen wandert, die ein Skript prüft —
|
||||
nachdem der Wert des alten Ansatzes (F-013…F-016, Meilenstein-/
|
||||
Prioritäts-Evidenz, Mirror-Topologie-Prosa) in neckbeard eingefaltet
|
||||
wurde.
|
||||
|
||||
### Akzeptanzkriterien (verifizierbar)
|
||||
|
||||
1. **Deterministische Gates grün:** `scripts/validate.py` meldet auf dem
|
||||
migrierten Repo 0 Fehler; `scripts/gen_status.py --check` meldet
|
||||
STATUS.md aktuell.
|
||||
2. **Entscheidungen portiert:** alle Entscheidungen aus `decisions/`
|
||||
liegen als ADRs mit schema-konformem Frontmatter unter `docs/adr/` —
|
||||
11/11 validieren *(bei Gate-1-Freigabe 10; `decisions/0011` kam am
|
||||
2026-08-11 hinzu, siehe Nachtrag in Gate 3)*.
|
||||
3. **Ein Backlog:** die fünf Arbeitspunkte aus F-004 (OVERMIND-01,
|
||||
CFGMON-11/12/13, MATRIX-05) existieren als Issues im kanonischen
|
||||
System — 5/5; 0 offene „Nächste Schritte" in `hosts/` ohne
|
||||
Issue-Referenz.
|
||||
4. **Generierte statt behaupteter Zustand:** 0 handgepflegte Zählungen
|
||||
und „Stand"-Etiketten in kanonischen Dateien, wo ein Generat sie
|
||||
ersetzt; kein kanonisches Dokument widerspricht dem Werkzeugstand
|
||||
bei den Meilensteinen (M1–M5).
|
||||
5. **Muster → Mechanismus:** für jedes Driftmuster A–D benennt das
|
||||
Design mindestens einen deterministischen Check, und pro Muster feuert
|
||||
mindestens ein Check nachweislich auf dem Vor-Migrations-Stand — 4/4
|
||||
demonstriert.
|
||||
6. **Ernte dokumentiert:** 7/7 neckbeard-Lücken mit Disposition
|
||||
(eingefaltet / als Framework-Issue notiert / verworfen mit Grund);
|
||||
4/4 Works-well-Befunde mit benanntem Erhaltungsmechanismus oder
|
||||
begründetem Verzicht.
|
||||
|
||||
### Nicht-Ziele
|
||||
|
||||
- **Keine Historien-Umschreibung.** Die F-002/F-003-Remediation ist ein
|
||||
eigener Vorgang mit eigener bindender Auflage (Zuordnung im Stil von
|
||||
`shared/commit-zuordnung-2026-08-07.md`); diese Undertaking darf ihr
|
||||
nur nicht im Weg stehen.
|
||||
- **Kein Push** nach git.lab oder Gitea; der Branch bleibt lokal bis zur
|
||||
Freigabe durch sorb.
|
||||
- **Keine Änderung am neckbeard-Upstream.** Lücken werden hier
|
||||
dispositioniert; sie dort einzureichen ist ein eigener Akt.
|
||||
- **Kein Rollout in die fünf Komponenten-Repos** über das hinaus, was
|
||||
die Shared-Ruleset-Entscheidung (Gate 2) zwingend erfordert; der
|
||||
Rollout wird als Folge-Issues angelegt, nicht hier gebaut.
|
||||
- **Kein Forge-Zustand wird zerstört:** keine Löschung von
|
||||
GitLab-Issues, Labels, Meilensteinen oder dem Board durch die
|
||||
Migration selbst.
|
||||
- **`analysis/` bleibt eingefroren** — der Branch von Session 1 wird
|
||||
weder verändert noch umgebaut.
|
||||
- **Kein inhaltliches Umschreiben** des Host-/Visions-/Verfahrenswissens:
|
||||
Umzug, Frontmatter und Korrektur werkzeugwidersprechender Aussagen ja,
|
||||
Neuformulierung nein.
|
||||
|
||||
### Ankündigung
|
||||
|
||||
Das Management-Repo der Gruppe axion1337.chat zieht auf das
|
||||
neckbeard-Framework um. Die vorhandene Disziplin — Meilensteinpflicht,
|
||||
Status-Disziplin, ADR-Pflicht, AARs — bleibt erhalten, wandert aber von
|
||||
Stellen, die nur ein Mensch prüfen kann, an Stellen, die ein Skript
|
||||
prüft: Frontmatter statt Prosa, generiertes STATUS.md statt
|
||||
handgepflegter Zählungen, `validate.py` statt Konventionstreue aus dem
|
||||
Gedächtnis. Die vier Driftmuster des Feldtests bekommen je einen
|
||||
deterministischen Check, und was der alte Ansatz besser kann als
|
||||
neckbeard, wird zuerst ins Framework eingefaltet statt verworfen.
|
||||
Zielgruppe sind sorb und alle Agenten-Sessions, die künftig von einer
|
||||
Quelle starten, die sich nicht selbst widerspricht.
|
||||
|
||||
### UI
|
||||
|
||||
Keine UI beteiligt — Artefakte sind Markdown-Dateien, die Oberfläche
|
||||
bleibt GitLab/Obsidian/Editor. Mockups entfallen.
|
||||
|
||||
## Gate 2 — Architektur
|
||||
|
||||
### Gelesen (Pflichtlektüre vor den Optionen)
|
||||
|
||||
Alt-Ansatz: `CLAUDE.md`, `roadmap.md`, `decisions/README.md` und die
|
||||
tragenden Entscheidungen 0001, 0002, 0005, 0009, 0010,
|
||||
[verfahren/refinement.md](../../wiki/admin/refinement.md),
|
||||
[verfahren/stillstandspruefung.md](../../wiki/admin/stillstandspruefung.md),
|
||||
`.gitlab-ci.yml`, Auszüge aus `hosts/`. Neckbeard v0.1.1: AGENTS.md,
|
||||
WORKFLOW.md, ADR-0001…0004/0006, `schema.yaml`, `validate.py`,
|
||||
`gen_status.py`, Schöpfungs-AAR, `docs/wiki/index.md`. Session-1-Daten
|
||||
(lesend vom Analyse-Branch): `gitlab_issues.json` — 111 Issues, 71
|
||||
offen; **71/71 mit genau einem Meilenstein (M1 19 · M2 22 · M3 4 ·
|
||||
M4 12 · M5 14) und 71/71 mit genau einer Priorität** (low 32,
|
||||
medium 34, high 5). Die Entscheidungen 0003/0004/0006/0007/0008 werden
|
||||
bei der Portierung (Gate 4) vollständig gelesen; sie tragen keine
|
||||
Architekturfrage dieser Undertaking.
|
||||
|
||||
### Ernte, Teil 1 — die Fehlermuster beider Ansätze
|
||||
|
||||
Wo genau versagte der alte Ansatz, was hält neckbeard dagegen, und wo
|
||||
bleibt auch mit neckbeard ein Loch:
|
||||
|
||||
| Muster | Wurzel im Alt-Ansatz | Neckbeard-Gegenstück | Verbleibendes Loch → Mechanismus dieser Migration |
|
||||
|---|---|---|---|
|
||||
| **A** — Doku nicht nachgezogen (F-001, F-005, F-007, F-010, F-017) | Zustand steht als behauptete Zahl/Prosa an mehreren Stellen; nichts vergleicht | Generiertes STATUS.md (`gen_status.py --check` in CI), ADRs nie editiert nur abgelöst | Prosa, die *Forge*-Zustand behauptet, prüft neckbeard nicht → Drift-Prüfung Repo↔GitLab; Meilenstein-Satz als Schema-Enum (eine Quelle); „Stand"-Etiketten entfallen ersatzlos (git log antwortet) |
|
||||
| **B** — Regel repo-weit, Anwendung Teilmenge (F-002, F-003, F-008, F-011) | Regel gilt „für alle Repos", kein Artefakt zählt die Repos auf, kein Skript läuft über alle | **Lücke** — ADR-0001 endet an der Repo-Grenze | Komponenten-Artefakt + Abgleich gegen die zur Laufzeit gelesene Gruppenliste ([ADR-0013](../../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)); Git-Hygiene-Prüfung (12:00Z-Zeitstempel, kanonische Identität) über alle deklarierten Repos |
|
||||
| **C** — zwei Backlogs (F-004, F-009) | `hosts/`-Markdown hielt „Nächste Schritte" neben dem Board | In-Repo-Issues, ein Ort | Wiki-Seiten können wieder Aufgabenprosa ansammeln → Prüfregel: Aufgaben-Marker („Nächster Schritt", offene Checkboxen) in Wiki-Seiten ohne Issue-Verweis sind ein Befund |
|
||||
| **D** — Artefakte ohne Eigentümer überleben (F-006, F-012) | Branches/SHA-Zitate hat niemand je gelesen | `warn_if_orphan` nur für Wiki-Seiten | Branch-Hygiene (Alter/Divergenz verwaister Branches) und SHA-Auflösung inkl. Zuordnungstabelle in der Prüf-Familie; Refinement-Agenda erhält den Punkt |
|
||||
|
||||
Die sieben neckbeard-Lücken, Disposition (Akzeptanzkriterium 6, 7/7):
|
||||
|
||||
| # | Lücke | Disposition |
|
||||
|---|---|---|
|
||||
| 1 | Viele Repos, ein Regelwerk | **Eingefaltet:** [ADR-0013](../../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md) (Pointer + Prüfung) |
|
||||
| 2 | Kein Komponenten-Artefakt | **Eingefaltet:** Schema-Typ `component`, `docs/components/` (ADR-0013) |
|
||||
| 3 | Kein Meilenstein-Konzept | **Eingefaltet:** Pflichtfeld `milestone` im Issue-Schema ([ADR-0012](../../adr/0012-issues-im-repo-gitlab-als-spiegel.md)) |
|
||||
| 4 | SHA-Zitate unaufgelöst | **Eingefaltet (projektseitig):** Prüfskript nach Vorbild `inv_shas.py`; Upstream-Kandidat |
|
||||
| 5 | Git-Hygiene außerhalb des Blickfelds | **Eingefaltet (projektseitig):** Hygiene-Prüfung in der CI-Familie; Upstream-Kandidat |
|
||||
| 6 | Externe Link-Ziele ungeprüft | **Teilweise eingefaltet:** Sperrliste stillgelegter Ziele (toter Gitea-Tracker, F-005) als deterministische Prüfung; echte Erreichbarkeitsprüfung **verworfen** (netzabhängig, nichtdeterministisch — widerspricht validate-Philosophie) |
|
||||
| 7 | Prioritätsfeld als YAGNI verworfen | **Eingefaltet:** Pflichtfeld `priority` — der Feldtest liefert die Evidenz (71/71, klar getrennt vom Meilenstein), die das Schöpfungs-AAR fürs Wiedervorlegen verlangte |
|
||||
| +8 | *(neu, diese Session)* `validate.py` lehnt Verzeichnis-Links ab | Migration ersetzt Verzeichnis- durch Datei-Ziele; Upstream-Kandidat (Meinungsfrage) |
|
||||
| +9 | *(neu)* Kein definierter Ort für Projektregeln im übernommenen AGENTS.md | Projektregeln als ausgewiesener eigener Abschnitt unter den unveränderten Upstream-Abschnitten; Upstream-Kandidat |
|
||||
|
||||
### Ernte, Teil 2 — Wert des Alt-Ansatzes, eingefaltet (4/4 + Zusatz)
|
||||
|
||||
| Wert | Erhaltungsmechanismus |
|
||||
|---|---|
|
||||
| **F-014** Issue-Hygiene (Meilensteinpflicht, eine Priorität, ein Status, WIP-Limit, keine ID-Wiederverwendung) | Wird von Konvention zu Schema: `milestone`/`priority` Pflichtfelder, Status-Enum, WIP-Limit als Validator-Regel, Duplikat-ID-Prüfung existiert in `validate.py` bereits; Board bleibt via Spiegel erhalten (ADR-0012) |
|
||||
| **F-013** Mirror-Topologie mit Begründung, Gegenargument, Rettungspfad | Alt-ADRs 0001/0004 werden unverändert portiert; die „Warum zwei Orte"-Prosa und der Rettungspfad ziehen als Wiki-Seiten um; Mirror-Sync bleibt Stillstandsprüfung |
|
||||
| **F-015** Rewrite-Zuordnung, 251/251 verifiziert | `shared/commit-zuordnung-2026-08-07.md` → `docs/sources/` (unveränderlich, agentenschreibgeschützt); SHA-Prüfung löst über die Tabelle auf; die Zuordnungs-Auflage für künftige Rewrites steht im portierten ADR-0009 |
|
||||
| **F-016** Redliche Selbstdokumentation | „Redlichkeit"-Regeln ziehen in den Projektabschnitt von AGENTS.md; AAR-/Retro-Kultur bleibt (AARs → `docs/aar/`, Retro-Protokolle → `docs/sources/`) |
|
||||
| Stillstandsprüfungs-Prinzipien | Bleiben wörtlich: Prüfungen nur aus realen Fällen; „kann nicht prüfen" ist Befund, nicht Skip; Abbruch statt stillem Überspringen; Projektliste zur Laufzeit. Die neuen Gruppen-Prüfungen (ADR-0013, Hygiene, Drift) treten dieser Familie bei |
|
||||
| Board-Pflege-Rechte (Zusage-Spalten nur sorb) | Prozessregel im AGENTS.md-Projektabschnitt; Refinement-Ablauf zieht als Wiki-Seite um und instanziiert die WORKFLOW-Agenda (Board rechts-nach-links, Nachziehen, Entscheidungsvorlagen mit Empfehlung, Datumspflicht) |
|
||||
| ADR-Pflicht bei dauerhaften Ausnahmen | Übernommen in den Projektabschnitt — neckbeard kennt diese Regel selbst nicht (Upstream-Kandidat) |
|
||||
|
||||
### Zielarchitektur
|
||||
|
||||
**Migrationslandkarte** (alt → neu; Inhalte unverändert, sofern nicht
|
||||
werkzeugwidersprechend — Nicht-Ziel „kein Umschreiben"):
|
||||
|
||||
| Alt | Neu |
|
||||
|---|---|
|
||||
| `CLAUDE.md` | Ein-Zeilen-Pointer; Regeln → `AGENTS.md` (Upstream-Abschnitte wörtlich + Abschnitt „Gruppenregeln"); Karpathy-Block wortgleich → `docs/sources/regelwerk/karpathy-guidelines.md`, aus AGENTS.md zitiert *(freigegeben von sorb, 2026-08-11)* |
|
||||
| `decisions/0001…0011` | `docs/adr/0001…0011`, Frontmatter ergänzt, Text unverändert; `decisions/` entfällt, Verweise nachgezogen |
|
||||
| `roadmap.md` | Bleibt als Linien/Reihenfolge-Prosa; alle Zählungen und „Stand"-Blöcke raus (→ generiertes STATUS.md); M5 statt „offene Frage" (F-001) |
|
||||
| `verfahren/aar/*` (5) | `docs/aar/*`, Frontmatter (`open`/`harvested` nach Retro-Lage) |
|
||||
| `verfahren/retro/*` | `docs/sources/protokolle/*` (unveränderliche Protokolle) |
|
||||
| `verfahren/refinement.md` | `docs/wiki/admin/refinement.md` |
|
||||
| `verfahren/deploy-uebergabe.md` | `docs/wiki/deployment/deploy-uebergabe.md` |
|
||||
| `verfahren/stillstandspruefung.md` | `docs/wiki/admin/stillstandspruefung.md` |
|
||||
| `verfahren/textbloecke.md` | `docs/wiki/admin/textbloecke.md` (Pfade angepasst) |
|
||||
| `verfahren/issue-migration/` | `docs/sources/migration/issue-migration/` |
|
||||
| `verfahren/aar-vorlage.md` | ersetzt durch neckbeards `docs/aar/template.md` |
|
||||
| `hosts/*` (4) | `docs/wiki/admin/<host>.md`; offene Arbeitspunkte → Issues (F-004, 5/5) |
|
||||
| `vision/*` (3) | `docs/wiki/vision/*` (neue Wiki-Area `vision` — Alt-Wert „eine Datei je Linie", altes ADR-0005) |
|
||||
| `shared/branding.md`, `lab-netzwerk.md`, `zone-axion1337.md` | `docs/wiki/architecture/*` |
|
||||
| `shared/commit-zuordnung-2026-08-07.md` | `docs/sources/migration/commit-zuordnung-2026-08-07.md` |
|
||||
| — *(neu)* | `docs/sources/upstream/neckbeard-v0.1.1/` — gepinnte Originale als Baseline für den Drift-Check |
|
||||
| — *(neu)* | `PROJECT.md` ✓, `WORKFLOW.md` (wörtlich v0.1.1), `schema.yaml` (v0.1.1 + ausgewiesene Erweiterungen), `STATUS.md` (generiert), `docs/components/` (6 Deklarationen: 5 Komponenten + management; `game-operating`/`gameserver` als `external`), `docs/issues/` (importierte offene management-Issues + F-004-Nachzügler) |
|
||||
| `scripts/stillstandspruefung.py`, `ci/` | Bleiben; dazu `validate.py`, `gen_status.py` (v0.1.1) und die neuen Prüfskripte; `.gitlab-ci.yml` erhält einen Offline-Job `validate` (jeder Push) neben der geplanten Stillstandsprüfung |
|
||||
|
||||
**Prüf-Architektur — zwei Familien, scharfe Grenze:**
|
||||
|
||||
- **Offline & deterministisch** (`validate.py`, `gen_status.py --check`,
|
||||
SHA-Auflösung, Wiki-Aufgabenmarker, Sperrlisten-Check): läuft bei
|
||||
jedem Push, braucht nur den Baum. Kein Netz, keine Uhrzeit.
|
||||
- **Verbund & Laufzeit** (Stillstandsprüfungs-Familie: Mirror-Sync,
|
||||
Issue-Drift Repo↔GitLab, Pointer-Präsenz, Gruppenliste↔`docs/components/`,
|
||||
Git-Hygiene über die Gruppe): geplant/manuell in der Lab-CI, Token
|
||||
über maskierte Variablen, **Abbruch statt stillem Skip**, Befund =
|
||||
rote Pipeline = Alarmanlage.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
S[Session-Start] --> A[CLAUDE.md → AGENTS.md<br/>+ PROJECT.md + STATUS.md]
|
||||
A --> W[Arbeit nach Gates<br/>Artefakte in docs/]
|
||||
W --> C[Commit 12:00Z]
|
||||
C --> V{CI: validate.py +<br/>gen_status --check}
|
||||
V -- rot --> W
|
||||
V -- grün --> M[Spiegel-Skript<br/>dry-run → sorb triggert]
|
||||
M --> B[GitLab-Board/Meilensteine<br/>= Ansicht, nicht Wahrheit]
|
||||
B --> R[Refinement sonntags<br/>Board + STATUS.md]
|
||||
R --> W
|
||||
P[Stillstandsprüfung + Gruppen-Checks<br/>geplant, Lab-CI] -. Befund = Issue .-> R
|
||||
```
|
||||
|
||||
### Entscheidungen
|
||||
|
||||
Die zwei tragenden Richtungsentscheidungen stehen als ADRs (Status
|
||||
`proposed`, werden mit diesem Gate wirksam):
|
||||
|
||||
- **[ADR-0012](../../adr/0012-issues-im-repo-gitlab-als-spiegel.md)** —
|
||||
Issues im Repo kanonisch (Management-Scope), GitLab als
|
||||
deterministisch bespielter Spiegel; Optionen A/B/C abgewogen im ADR.
|
||||
- **[ADR-0013](../../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)** —
|
||||
Gruppenregeln kanonisch hier, Komponenten tragen Pointer, ein
|
||||
Komponenten-Artefakt macht die Gruppe prüfbar; Kopie/Submodule
|
||||
verworfen im ADR.
|
||||
|
||||
Feature-lokale Entscheidungen (bleiben hier):
|
||||
|
||||
1. **Framework-Dateien wörtlich** übernehmen (AGENTS.md-Abschnitte 1–5,
|
||||
WORKFLOW.md, Templates, Skripte) — jede Abweichung vom Upstream
|
||||
bleibt per Diff gegen v0.1.1 sichtbar; Projektspezifika leben
|
||||
ausschließlich im ausgewiesenen AGENTS-Abschnitt, in ADRs, Wiki und
|
||||
`schema.yaml`-Erweiterungen.
|
||||
2. **Issue-Nummern:** GitLab-iid = Datei-id für Importierte; neue Issues
|
||||
zählen ab Maximum weiter; `gitlab_iid`-Feld hält die Spiegelung.
|
||||
Keine dritte Nummernwelt, keine ID-Wiederverwendung.
|
||||
3. **Status-Enum erweitert** um `next` und `waiting` (Grund-Pflicht bei
|
||||
`waiting`) — die Board-Spalten sind belegter Alt-Wert; ein Mapping
|
||||
auf nur `open/in-progress` würde die einzige Zusage-Semantik
|
||||
(`status:next`) wegwerfen.
|
||||
4. **Slugs werden nicht umbenannt** (F-008): Rename = Forge-Eingriff,
|
||||
eigenes Issue; das Komponenten-Artefakt dokumentiert den Ist-Stand.
|
||||
5. **Verzeichnis-Links** in Prosa werden auf Datei-Ziele umgestellt
|
||||
(Lücke +8).
|
||||
6. **`analysis/` und `drafts/`** des Analyse-Branches bleiben dort;
|
||||
nichts davon wird auf diesen Branch geholt.
|
||||
7. **`docs/sources/` wird nach Quellenart untergliedert** (Vorschlag
|
||||
sorb, 2026-08-11): `regelwerk/` (wortgleiche Regeltexte),
|
||||
`upstream/` (gepinnte Framework-Originale), `protokolle/`
|
||||
(Retro-/Workshop-Protokolle), `migration/` (Zuordnungen,
|
||||
Umzugsunterlagen). Kriterium bleibt *lebendig → Wiki, unveränderlich
|
||||
→ sources*; AARs sind Artefakte mit Lebenszyklus, keine Quellen.
|
||||
8. **Drift-Check gegen Upstream-Baseline:** die v0.1.1-Originale liegen
|
||||
unter `docs/sources/upstream/neckbeard-v0.1.1/`; ein Offline-Check
|
||||
vergleicht die Instruktionsdateien (CLAUDE.md-Pointer, AGENTS.md bis
|
||||
zur Projektabschnitts-Marke, WORKFLOW.md, Templates) byteweise.
|
||||
Stilles Umschreiben durch eine Session wird damit roter Befund;
|
||||
Framework-Upgrade = bewusste Baseline-Aktualisierung. `schema.yaml`
|
||||
und die Skripte sind **erklärt projekterweitert** — Original liegt
|
||||
zur Diffbarkeit bei, wird aber nicht byte-erzwungen.
|
||||
9. **AGENTS.md-Änderungsschutz:** die alte Fußzeilen-Regel zieht in den
|
||||
Projektabschnitt um — Änderungen an AGENTS.md nur mit sorb
|
||||
abgestimmt.
|
||||
10. **Issue-Import liest Beschreibungstexte** der offenen
|
||||
management-Issues read-only über den Token (freigegeben von sorb,
|
||||
2026-08-11); Kommentare bleiben auf GitLab, der Tokenwert erscheint
|
||||
nirgends.
|
||||
|
||||
### Constraints
|
||||
|
||||
- Mirror-Topologie unangetastet: Flux-Quelle bleibt Gitea, keine
|
||||
direkten Gitea-Pushes, Kanonisierungs-Verfahren gilt weiter.
|
||||
- Kein API-Schreibzugriff ohne menschlichen Trigger; das Spiegel-Skript
|
||||
hat `--dry-run` als Default. Diese Session pusht nichts.
|
||||
- Commit-Konventionen (englisch, 12:00:00 UTC, kanonische Identität)
|
||||
gelten für jeden Migrations-Commit.
|
||||
- Artefaktsprache Deutsch (`PROJECT.md`), Upstream-Framework-Texte
|
||||
bleiben englisch — der Diff-Abgleich gegen v0.1.1 wiegt schwerer als
|
||||
Sprachreinheit.
|
||||
- Secrets-Regeln unverändert (Token nur per Pfad/maskierter Variable).
|
||||
- Die Historien-Remediation (F-002/F-003) bleibt draußen; jedes künftige
|
||||
Rewrite trägt die Zuordnungs-Auflage (portiertes ADR-0009).
|
||||
|
||||
### Rückmeldungen an neckbeard (Kandidaten, eigener Akt — nicht Teil dieser Undertaking)
|
||||
|
||||
Lücken 1–5 und 7 mit Feldtest-Evidenz, dazu +8 (Verzeichnis-Links), +9
|
||||
(Ort für Projektregeln), die fehlende ADR-Pflicht bei dauerhaften
|
||||
Ausnahmen, und als Erfahrungswert: die Stillstandsprüfungs-Prinzipien
|
||||
(Prüfungen nur aus realen Fällen; Abbruch statt Skip) als Muster für
|
||||
eine künftige Laufzeit-Prüf-Familie neben `validate.py`.
|
||||
|
||||
## Gate 3 — Programm-Design
|
||||
|
||||
### Dateiorte (vollständig)
|
||||
|
||||
**Wurzel — neu:** `AGENTS.md` (Upstream §1–5 wörtlich, dann Marke
|
||||
`<!-- projektabschnitt -->`, dann „§6 Gruppenregeln"), `WORKFLOW.md`
|
||||
(wörtlich v0.1.1), `schema.yaml` (v0.1.1 + Erweiterungen, im Kopf
|
||||
ausgewiesen), `STATUS.md` (generiert).
|
||||
**Wurzel — geändert:** `CLAUDE.md` → Pointer (wörtlich v0.1.1),
|
||||
`roadmap.md` (Zahlen/„Stand" raus, M5 rein, Datei-Links),
|
||||
`README.md` (Pfade/Struktur nachgezogen, Datei-Links),
|
||||
`.gitlab-ci.yml` (+ Job `validate`, Stage `pruefen`, bei jedem Push).
|
||||
**Wurzel — entfällt (git mv):** `decisions/`, `hosts/`, `verfahren/`,
|
||||
`vision/`, `shared/`.
|
||||
|
||||
**`docs/adr/`:** `0001…0011` portiert (Frontmatter ergänzt; Datum =
|
||||
Original-Datum; Body unverändert bis auf umgezogene Link-Ziele),
|
||||
`0012`/`0013` (accepted), `template.md` (v0.1.1).
|
||||
**`docs/aar/`:** die 6 AARs aus `verfahren/aar/` (Dateinamen bleiben,
|
||||
Frontmatter: die vier vom 2026-08-01/02 `harvested` — von der Retro
|
||||
2026-08-09 geerntet; `2026-08-09-refinement-und-betrieb.md` und
|
||||
`2026-08-11-apo-calls-profile-zeile.md` `open`),
|
||||
`template.md` (v0.1.1; ersetzt `aar-vorlage.md`).
|
||||
**`docs/issues/`:** Import aller offenen management-Issues als
|
||||
`NNNN-slug.md` (NNNN = GitLab-iid, vierstellig; Slug deterministisch
|
||||
aus dem Titel: Kleinbuchstaben, Umlaute ae/oe/ue/ss, sonst `-`,
|
||||
Alt-IDs bleiben im Titel), plus 5 neue Issues für die
|
||||
F-004-Arbeitspunkte (IDs ab max(iid)+1), `template.md` (v0.1.1).
|
||||
**`docs/components/`:** `management.md`, `threadnet-call.md`,
|
||||
`thread-net-git.md`, `threadnet-operating.md`,
|
||||
`axion1337.chat-gitops.md`, `ThreadNet-Web.md` (Dateiname = Slug,
|
||||
buchstabengetreu), dazu `game-operating.md`, `gameserver.md`
|
||||
(`phase: external`).
|
||||
**`docs/wiki/`:** `index.md` (projektangepasst: Area-Tabelle + `vision`;
|
||||
nicht in der Baseline), `admin/`: `cfgmon.md`, `game.md`, `matrix.md`,
|
||||
`overmind.md`, `refinement.md`, `stillstandspruefung.md`,
|
||||
`textbloecke.md`; `deployment/`: `deploy-uebergabe.md`;
|
||||
`architecture/`: `branding.md`, `lab-netzwerk.md`, `zone-axion1337.md`;
|
||||
`vision/`: `axion1337-chat.md`, `homelab.md`, `threadnet.md`.
|
||||
**`docs/sources/`:** `regelwerk/karpathy-guidelines.md`;
|
||||
`upstream/neckbeard-v0.1.1/` (AGENTS.md, CLAUDE.md, WORKFLOW.md,
|
||||
schema.yaml, die 4 Templates, validate.py, gen_status.py, dazu
|
||||
`HERKUNFT.md` mit Tag/SHA); `protokolle/retro-2026-08-09.md`;
|
||||
`migration/commit-zuordnung-2026-08-07.md`,
|
||||
`migration/issue-migration/README.md`,
|
||||
`migration/import_issues.py` + `migration/issue-import-protokoll.md`
|
||||
(Einmal-Werkzeug und sein Protokoll — Aufzeichnung, kein Dauerbetrieb).
|
||||
**`scripts/`:** `validate.py` (v0.1.1 + 3 Regeln), `gen_status.py`
|
||||
(v0.1.1 + Meilenstein-/Prioritätsspalten und -verteilung),
|
||||
`pruefe_upstream_drift.py` (neu), `pruefe_prosa.py` (neu),
|
||||
`gruppenpruefung.py` (neu), `spiegel_issues.py` (neu);
|
||||
`stillstandspruefung.py` unangetastet.
|
||||
|
||||
### Schema-Erweiterungen (exakt)
|
||||
|
||||
```yaml
|
||||
# issue — zusätzlich:
|
||||
required: [type, id, status, created, milestone, priority]
|
||||
status: { enum: [open, next, in-progress, waiting, done, rejected] }
|
||||
milestone: { enum: [M1, M2, M3, M4, M5] }
|
||||
priority: { enum: [high, medium, low] }
|
||||
due: { kind: date, nullable: true }
|
||||
host: { enum: [cfgmon, overmind, matrix, game], nullable: true }
|
||||
area: { enum: [security, infrastructure, database, element], nullable: true }
|
||||
wartegrund: { kind: str, nullable: true }
|
||||
gitlab_iid: { pattern: "^\\d+$", nullable: true }
|
||||
rules: [waiting_requires_reason] # + global: wip_limit
|
||||
# component — neuer Typ:
|
||||
component:
|
||||
dir: "docs/components"
|
||||
filename: "^[A-Za-z0-9.-]+\\.md$"
|
||||
required: [type, slug, anzeigename, phase]
|
||||
fields:
|
||||
slug: { kind: str } # rule: slug_matches_filename
|
||||
anzeigename: { kind: str }
|
||||
phase: { enum: [active, staged, external] }
|
||||
gitlab: { kind: str }
|
||||
mirror: { kind: str, nullable: true }
|
||||
related: { kind: links }
|
||||
rules: [slug_matches_filename]
|
||||
# wiki-page.area — Enum + vision
|
||||
```
|
||||
|
||||
### Signaturen (keine Rümpfe)
|
||||
|
||||
```text
|
||||
validate.py [repo-root] # + Regeln: wip_limit (≤2 in-progress, repoweit),
|
||||
# waiting_requires_reason, slug_matches_filename
|
||||
gen_status.py [--check] [repo-root] # Issues-Tabelle + Spalten milestone/priority
|
||||
# + Verteilungszeile je Meilenstein
|
||||
pruefe_upstream_drift.py [repo-root] # Byte-Vergleich Arbeitsdatei ↔ sources/upstream;
|
||||
# AGENTS.md: Präfix bis Marke; exit 1 bei Abweichung
|
||||
pruefe_prosa.py [repo-root] # (a) SHA-Zitate in docs/** + Wurzel-*.md auflösen
|
||||
# (git cat-file, sonst Zuordnungstabelle, sonst FEHLER)
|
||||
# (b) Aufgabenmarker in docs/wiki/** ohne Issue-Verweis
|
||||
# (c) Sperrliste stillgelegter URL-Muster (toter Gitea-Tracker)
|
||||
gruppenpruefung.py # Lab-CI, Token aus Umgebung, Abbruch ohne Token:
|
||||
# Gruppenliste (Laufzeit) ↔ docs/components/;
|
||||
# Pointer-Präsenz je active/staged-Komponente;
|
||||
# Issue-Drift docs/issues ↔ GitLab (Titel/Status/
|
||||
# Meilenstein/Priorität); Meilenstein- und
|
||||
# Prioritätspflicht über ALLE offenen Gruppen-Issues
|
||||
# (realer Fall: gitops#61, siehe Nachtrag);
|
||||
# Git-Hygiene (Commits nach
|
||||
# 2026-08-07 ≠ 12:00:00Z oder fremde Identität = Befund)
|
||||
spiegel_issues.py [--ausfuehren] # Default Dry-Run: druckt geplante API-Aufrufe;
|
||||
# --ausfuehren nur durch sorb; Repo → GitLab, nie zurück
|
||||
```
|
||||
|
||||
**CI-Fluss:** Job `validate` (jeder Push, offline):
|
||||
`validate.py && gen_status.py --check && pruefe_upstream_drift.py &&
|
||||
pruefe_prosa.py`. Job `stillstandspruefung` (geplant/manuell) wie
|
||||
bisher; `gruppenpruefung` daneben, gleiche Regeln (rot = Alarm,
|
||||
Abbruch statt Skip).
|
||||
|
||||
### Was die Prüfungen zusichern (inkl. Muster-Demonstration, Kriterium 5)
|
||||
|
||||
| Prüfung | Zusicherung / Demo |
|
||||
|---|---|
|
||||
| Negativtests (Scratch-Bäume, je Regel einer) | 3× in-progress → Fehler; `waiting` ohne `wartegrund` → Fehler; Component-Slug ≠ Dateiname → Fehler; 1 Byte Abweichung in WORKFLOW.md → Fehler; Schöpfungs-AAR-Lehre: grüner Validator ohne Negativtest zählt nicht |
|
||||
| **Muster A** | Meilenstein-Abgleich gegen den eingefrorenen Session-1-Export: Alt-`CLAUDE.md` („M1–M4") ↔ Export (M5 existiert) → feuert |
|
||||
| **Muster B** | Git-Hygiene über die lokalen Komponenten-Klone → feuert (Erwartung: die 237 Echtzeit-Commits aus F-002) |
|
||||
| **Muster C** | `pruefe_prosa.py` auf dem Vor-Migrations-Stand von `hosts/` → feuert auf die 5 F-004-Punkte; nach Migration: 0 |
|
||||
| **Muster D** | SHA-Auflösung auf Vor-Migrations-Stand → feuert auf die 6 verwaisten Zitate aus F-012; Auflösung über die Zuordnungstabelle nachgewiesen |
|
||||
|
||||
### DO NOT CHANGE
|
||||
|
||||
- Der Analyse-Branch und alles unter `analysis/`.
|
||||
- Substanz der portierten Texte: ADR-Bodies, AARs, Retro, Zuordnung,
|
||||
Karpathy-Block, Hosts-/Visions-Prosa — nur Umzug, Frontmatter,
|
||||
Link-Ziele; inhaltliche Korrektur **nur** wo ein Dokument dem
|
||||
Werkzeugstand widerspricht (roadmap M5, Alt-CLAUDE-Regeln gehen in
|
||||
AGENTS §6 in korrigierter Fassung).
|
||||
- `scripts/stillstandspruefung.py`, `ci/lab-ca-chain.crt`,
|
||||
`.gitlab/issue_templates/` — unangetastet.
|
||||
- GitLab-Zustand: kein Issue, Label, Meilenstein, Board wird verändert;
|
||||
`spiegel_issues.py` läuft in dieser Undertaking nur als Dry-Run.
|
||||
- Kein `git push`; Tokenwert erscheint in keiner Ausgabe.
|
||||
- Upstream-Framework-Texte §1–5 / WORKFLOW / Templates: byte-treu.
|
||||
|
||||
### Wackligste Annahmen (benannt, Stand Gate 3)
|
||||
|
||||
1. **AAR-Erntestatus**: „die vier alten AARs sind geerntet" schließe ich
|
||||
aus der Retro-Existenz, nicht aus einer Erntemarke — sorb kann das
|
||||
im Refinement kippen.
|
||||
2. **Enums aus dem Ist-Stand eingefroren** (host/area/M1–M5): jeder
|
||||
neue Host oder Meilenstein braucht künftig einen Schema-Commit.
|
||||
Gewollt (sichtbare Änderung), aber Reibung.
|
||||
3. **Slug-Erzeugung aus deutschen Titeln** muss deterministisch und
|
||||
kollisionsfrei sein; bei Kollision entscheidet die iid, nicht der
|
||||
Slug.
|
||||
4. **Git-Hygiene per API vs. lokale Klone**: die Demo läuft auf den
|
||||
lokalen Klonen; die CI-Fassung per API kann bei großen Historien
|
||||
paginieren müssen — begrenzt auf Commits seit 2026-08-07.
|
||||
5. **Verdichtung von Alt-CLAUDE.md nach AGENTS §6**: Welche Sätze
|
||||
Regelrang behalten und welche ins Wiki wandern, ist Urteilssache;
|
||||
Volltext überlebt in ADRs/Wiki/sources, aber eine tragende Nuance
|
||||
könnte aus dem Immer-geladen-Teil fallen.
|
||||
6. **`gen_status.py`-Fork-Tiefe**: je mehr das Generat zeigt, desto
|
||||
weiter entfernt es sich vom Upstream; gewählt ist die kleinste
|
||||
Erweiterung, die die Roadmap-Zahlen ersetzt.
|
||||
|
||||
### Nachtrag 2026-08-11 — die Realität lief nach Gate-3-Freigabe weiter
|
||||
|
||||
Hinweis von sorb bei der Gate-3-Freigabe, per Fetch und Live-API
|
||||
(read-only) verifiziert:
|
||||
|
||||
- **management `main` +3 Commits:** AAR
|
||||
`2026-08-11-apo-calls-profile-zeile.md` (+ Nachtrag) und — kritisch —
|
||||
**`decisions/0011`** (Enrollment-Localpart-Kollision). Das alte Schema
|
||||
zählt parallel weiter; die Session-ADRs kollidierten mit der Nummer
|
||||
und wurden zu **0012/0013** umnummeriert (genau die Duplikat-ID-Klasse,
|
||||
die `validate.py` künftig mechanisch meldet). Branch auf
|
||||
`origin/main` rebasiert.
|
||||
- **gitops +2 Commits** (MAS-Fix, Runbook); beide und alle drei
|
||||
management-Commits halten die Hygiene-Regeln (12:00:00Z, kanonische
|
||||
Identität) — geprüft.
|
||||
- **Live-Backlog: 72 offen** (Import zählt beim Lauf, nicht aus diesem
|
||||
Text). **gitops#61 trägt keinen Meilenstein** und das neue Label
|
||||
`area:authentik` — die 100%-Meilenstein-Disziplin (F-014) ist binnen
|
||||
zwei Tagen real gerissen. Konsequenz: `gruppenpruefung.py` prüft die
|
||||
Meilenstein-/Prioritätspflicht über alle offenen Gruppen-Issues (der
|
||||
reale Fall, den die Stillstandsprüfungs-Regel für neue Prüfungen
|
||||
verlangt, existiert hiermit). Management-Scope: 26 offene Issues,
|
||||
iids 1–32.
|
||||
- Zahlen im Dokument nachgezogen: 11 Alt-ADRs, 6 AARs,
|
||||
Akzeptanzkriterium 2 = 11/11. Die Enums bleiben, wie in Annahme 2
|
||||
benannt, aus dem management-Scope abgeleitet; `area:authentik` liegt
|
||||
außerhalb (gitops) und wird erst bei dessen Adoption Schema-Thema.
|
||||
|
||||
## Gate 4 — Vertikale Slices
|
||||
|
||||
Jeder Slice endet mit Nachweis, Status und **STOP**.
|
||||
|
||||
**Slice 1 — Tracer Bullet: die Framework-Kette läuft Ende-zu-Ende.**
|
||||
Baseline (`docs/sources/upstream/neckbeard-v0.1.1/` + `HERKUNFT.md`),
|
||||
Karpathy-Block wortgleich nach `docs/sources/regelwerk/`, `AGENTS.md`
|
||||
(§1–5 byte-treu + §6 Gruppenregeln), `CLAUDE.md`-Pointer, `WORKFLOW.md`,
|
||||
Templates, erweitertes `schema.yaml`, `validate.py` (+3 Regeln),
|
||||
`gen_status.py` (Fork), `pruefe_upstream_drift.py`, generiertes
|
||||
`STATUS.md`, CI-Job `validate`, README-Verzeichnis-Link entschärft.
|
||||
*Verify:* validate 0 Fehler · gen_status --check aktuell · Drift-Check
|
||||
grün · vier Negativtests feuern · Baseline byte-identisch zur Referenz.
|
||||
|
||||
**Slice 2 — ADR-Port.** `decisions/0001…0011` → `docs/adr/` mit
|
||||
Frontmatter, Verweise nachgezogen, `decisions/` entfällt.
|
||||
*Verify:* 11/11 validieren, Duplikat-ID-Prüfung greift, validate grün.
|
||||
|
||||
**Slice 3 — Wiki, Sources, AARs.** `verfahren/`/`hosts/`/`vision/`/
|
||||
`shared/` an ihre Zielorte, Wiki-Index, `pruefe_prosa.py`; Demos
|
||||
Muster C (F-004-Punkte auf Vor-Stand) und D (6 verwaiste SHAs).
|
||||
*Verify:* validate + pruefe_prosa grün auf Endstand, Demos feuern auf
|
||||
Vor-Stand, alte Wurzelordner leer.
|
||||
|
||||
**Slice 4 — Issue-Import.** `import_issues.py` liest die offenen
|
||||
management-Issues live (read-only), 26+ Dateien + 5 F-004-Issues,
|
||||
`roadmap.md` verliert Zahlen an STATUS.md.
|
||||
*Verify:* alle Issue-Dateien validieren (Pflicht-Meilenstein/-Priorität),
|
||||
Import-Protokoll unter sources/migration, Muster-C-Endstand = 0.
|
||||
|
||||
**Slice 5 — Komponenten, Gruppenprüfung, Spiegel.** 8
|
||||
Komponenten-Deklarationen, `gruppenpruefung.py` (+ CI-Job),
|
||||
`spiegel_issues.py` (Dry-Run-Demo); Demos Muster A (eingefrorener
|
||||
Export ↔ Alt-CLAUDE) und B (Hygiene über lokale Klone), Live-Befund
|
||||
gitops#61.
|
||||
*Verify:* Dry-Run-Ausgabe plausibel, Demos feuern, kein API-Write.
|
||||
|
||||
Alle fünf Slices sind mit Nachweis und STOP abgenommen worden
|
||||
(Freigaben sorb, 2026-08-11); die Commits `e36ed33`…`865d761` tragen
|
||||
die Evidenz je Slice im Commit-Text.
|
||||
|
||||
## Gate 5 — Closeout (AAR)
|
||||
|
||||
### Geplant
|
||||
|
||||
Gate 0–5 nach WORKFLOW.md; Zwei-Wege-Ernte vor Übernahme (bindende
|
||||
Vorgabe der Session-1-Übergabe); fünf Slices; sechs Akzeptanzkriterien.
|
||||
|
||||
### Tatsächlich
|
||||
|
||||
Alle Gates und Slices wie geplant, mit vier realitätsgetriebenen
|
||||
Abweichungen:
|
||||
|
||||
1. **Die Realität lief während der Undertaking weiter** (Hinweis sorb
|
||||
bei Gate-3-Freigabe): `main` +3 Commits mit `decisions/0011` →
|
||||
Nummernkollision mit den Session-ADRs, Umnummerierung auf 0012/0013,
|
||||
Rebase; gitops#61 entstand **ohne Meilenstein** und riss die
|
||||
100%-Disziplin aus F-014 binnen zwei Tagen — es wurde der reale Fall
|
||||
für die neue gruppenweite Pflicht-Prüfung.
|
||||
2. **F-004 war feiner als der Befund:** MATRIX-05 seit 2026-08-01
|
||||
erledigt (kein Issue nötig — „Alles *Offene* ist ein Issue"),
|
||||
CFGMON-12/13 bereits per git.lab-Issues verfolgt (nur die toten
|
||||
Gitea-Links verdeckten das). Statt 5/5 neuen Issues: 2 neue (0033,
|
||||
0034), 2 verifizierte Verweise, 1 begründeter Verzicht — mit sorb
|
||||
abgestimmt; Details im
|
||||
[Import-Protokoll](../../sources/migration/issue-import-protokoll.md).
|
||||
3. **F-005 war größer als der Befund:** nicht ein toter Tracker-Link,
|
||||
sondern acht, quer durch Host-Seiten und einen importierten
|
||||
Issue-Fußtext; zwei per Live-Titelabgleich verifiziert umgezogen,
|
||||
sechs zu ehrlichen Historien-Zitaten entschärft.
|
||||
4. **Hex ist nicht gleich Git-SHA:** die SHA-Prüfung fand eine
|
||||
Authentik-uid und zwei Alertmanager-Silence-IDs — gelöst über die
|
||||
kuratierte Ausnahmenliste mit Grund je Zeile statt über eine
|
||||
schlauere Heuristik.
|
||||
|
||||
Akzeptanzkriterien: **6/6 erfüllt** — (1) alle deterministischen Gates
|
||||
grün; (2) 11/11 ADRs portiert; (3) 0 issuelose Arbeitspunkte im Wiki,
|
||||
F-004-Disposition dokumentiert; (4) 0 Handzählungen, kein Dokument
|
||||
widerspricht dem Werkzeugstand M1–M5; (5) 4/4 Muster-Demos gefeuert
|
||||
(A: „M1–M4"↔M5-Export; B: 222 Echtzeit-Commits, deckungsgleich mit den
|
||||
Session-1-Zahlen; C: 6→0 Aufgabenblöcke; D: verwaiste SHAs aufgelöst
|
||||
oder kuratiert); (6) 9/9 Lücken-Dispositionen und 4/4
|
||||
Erhaltungsmechanismen in Gate 2, final abgehakt.
|
||||
|
||||
### Warum die Differenz
|
||||
|
||||
Die Undertaking hat einen lebenden Verbund migriert, keinen
|
||||
eingefrorenen: Jede Abweichung entstand daraus, dass zwischen Analyse
|
||||
(2026-08-09/10) und Bau (2026-08-11) weitergearbeitet wurde. Genau die
|
||||
Driftklassen, die die Migration schließen soll, traten währenddessen
|
||||
frisch auf — und wurden zu Testfällen statt zu Störungen.
|
||||
|
||||
### Lehren (geerntet nach [Stolpersteine](../../wiki/stolpersteine/neckbeard-migration.md))
|
||||
|
||||
- Ein hexförmiges Wort ist nicht automatisch ein Git-SHA; kuratierte
|
||||
Ausnahmen mit Grund schlagen schlauere Raterei.
|
||||
- Der Link-Checker ist das beste Umzugswerkzeug: erst bewegen, dann die
|
||||
gemeldeten Ziele reihum fixen — kein Verweis blieb offen.
|
||||
- Frische Importe sind Prüfmaterial: beide Prosa-Prüfungen fanden auf
|
||||
den eben importierten Texten sofort echte Fälle.
|
||||
- Bestätigt aus dem Upstream-Schöpfungs-AAR: ein grüner Validator zählt
|
||||
erst mit Negativtests (vier gebaut, alle feuern).
|
||||
- `gen_status.py` braucht lokal Python ≥ 3.10 (`write_text(newline=)`);
|
||||
CI nutzt 3.12, lokal läuft ein venv.
|
||||
- Session-1-Lehre erneut bestätigt: `TZ` gehört an den git-Prozess
|
||||
(`--date=format-local` + `TZ=UTC` in `gruppenpruefung.py`).
|
||||
|
||||
### Wackligste Entscheidungen dieser Session (WORKFLOW.md, Session-Ende)
|
||||
|
||||
1. **AAR-Erntestatus der vier alten AARs** aus der Retro-Existenz
|
||||
geschlossen, nicht aus einer Erntemarke — beim nächsten Refinement
|
||||
gegenprüfen.
|
||||
2. **Die §6-Verdichtung der Alt-CLAUDE.md**: von sorb quergelesen und
|
||||
freigegeben, aber ob jede tragende Nuance den Sprung geschafft hat,
|
||||
zeigt erst der Betrieb.
|
||||
3. **Generische `wartegrund`-Platzhalter** bei 7 importierten
|
||||
waiting-Issues — Issue 0041.
|
||||
4. **Die fünf eigenen Identitäten stehen als Konstante im
|
||||
Hygiene-Skript** — bei einer künftigen Identitäts-Remediation
|
||||
(F-003) muss die Liste mitgepflegt werden.
|
||||
5. **Spiegelumfang bewusst schmal** (keine Beschreibungen): richtig für
|
||||
Kommentar-Erhalt, heißt aber, dass Beschreibungs-Änderungen im Repo
|
||||
auf GitLab nicht sichtbar werden — Board-Nutzer sehen den Stand der
|
||||
Migration, nicht jede Textpflege.
|
||||
|
||||
### Offene Folgearbeit (als Issues, nicht als Prosa)
|
||||
|
||||
[0035–0039](../../issues/0035-rollout-agents-pointer-axion1337-chat-gitops.md) Pointer-Rollout je Komponente (fünf Issues) ·
|
||||
[0040](../../issues/0040-neckbeard-rueckmeldungen-einreichen.md)
|
||||
neckbeard-Rückmeldungen einreichen ·
|
||||
[0041](../../issues/0041-wartegrund-der-importierten-waiting-issues.md)
|
||||
wartegrund präzisieren ·
|
||||
[0042](../../issues/0042-migration-in-betrieb-nehmen-push-spiegel-schedule.md)
|
||||
Inbetriebnahme (Push, erster Spiegel-Lauf, CI-Schedule).
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
type: design
|
||||
status: gate-1 # gate-1 | gate-2 | gate-3 | gate-4 | gate-5 | done
|
||||
date: YYYY-MM-DD
|
||||
size: L # this template is for size L
|
||||
related: [] # issues, ADRs spawned or read
|
||||
---
|
||||
|
||||
<!-- Copy to docs/design/YYYY-MM-DD-slug.md. Delete comments when filling in.
|
||||
Fill ONE gate at a time; each gate ends with STOP — do not pre-fill
|
||||
later gates. Advance `status` only after human approval. -->
|
||||
|
||||
# Design: Title
|
||||
|
||||
## Gate 1 — Product
|
||||
|
||||
**Problem.** <!-- What user problem, for whom. -->
|
||||
|
||||
**Acceptance criterion.** <!-- Verifiable. A real number where one
|
||||
exists; otherwise a concretely checkable outcome. "Works" is not one. -->
|
||||
|
||||
**Non-goals.** <!-- What this deliberately does NOT do. The cheapest
|
||||
scope-creep brake there is. -->
|
||||
|
||||
**Announcement.** <!-- 3–5 sentences: what it is, who it's for, why
|
||||
it's good. Can't write it? The product isn't understood yet. -->
|
||||
|
||||
**Mockups.** <!-- Only if UI is involved: plain-HTML mockups, linked. -->
|
||||
|
||||
> **STOP — awaiting Gate 1 approval.**
|
||||
|
||||
## Gate 2 — Architecture
|
||||
|
||||
**Inputs read.** <!-- Which ADRs and AARs were read; one line each on
|
||||
why they matter here. -->
|
||||
|
||||
**System fit.** <!-- Endpoints, tables/schemas, query outlines,
|
||||
end-to-end flow as Mermaid. Against the actual codebase. -->
|
||||
|
||||
**Constraints.** <!-- Non-functional, proportional to the project:
|
||||
performance, security, operations, compatibility. "None relevant"
|
||||
is a valid answer — but say it. -->
|
||||
|
||||
**Options & trade-offs.** <!-- Where more than one viable way exists:
|
||||
name the options, pro/contra each, state the chosen one and WHY.
|
||||
This is the feature-local decision record. Only lasting, binding
|
||||
decisions graduate to an ADR below. -->
|
||||
|
||||
**New ADRs.** <!-- Lasting decisions discovered here → one ADR each,
|
||||
linked. None is a valid answer. -->
|
||||
|
||||
> **STOP — awaiting Gate 2 approval.**
|
||||
|
||||
## Gate 3 — Program Design
|
||||
|
||||
**Files.** <!-- Exact paths, new and touched. -->
|
||||
|
||||
**Signatures.** <!-- Types and method signatures, no bodies. -->
|
||||
|
||||
**Call stack.** <!-- For the main flow(s). -->
|
||||
|
||||
**Test assertions.** <!-- What the tests will assert. -->
|
||||
|
||||
**Boundaries — DO NOT CHANGE.** <!-- Explicit list. -->
|
||||
|
||||
**Shakiest calls.** <!-- The decisions you are least confident about. -->
|
||||
|
||||
> **STOP — awaiting Gate 3 approval.**
|
||||
|
||||
## Gate 4 — Vertical Slices
|
||||
|
||||
<!-- Slice 1 is the tracer bullet: thin end-to-end, runs with mocks.
|
||||
Then real logic, one testable slice at a time. Per task:
|
||||
files / action / verify / done. After each slice: evidence,
|
||||
status, STOP. -->
|
||||
|
||||
### Slice 1 — Tracer bullet
|
||||
- [ ] Task: … — files: … — action: … — verify: … — done: …
|
||||
|
||||
**Evidence:** <!-- command output, test run, screenshot ref -->
|
||||
**Status:** <!-- DONE | DONE_WITH_CONCERNS | NEEDS_CONTEXT | BLOCKED -->
|
||||
|
||||
> **STOP — slice review.**
|
||||
|
||||
### Slice 2 — …
|
||||
|
||||
### Handoff
|
||||
|
||||
<!-- The single place session state lives. Overwrite on every handoff;
|
||||
git keeps the history.
|
||||
Done slices: …
|
||||
Open decisions: …
|
||||
Next step: … -->
|
||||
|
||||
## Gate 5 — Closeout (AAR)
|
||||
|
||||
**Planned vs. actual.** <!-- What was planned, what happened. -->
|
||||
|
||||
**Why the difference.** <!-- Root causes, honestly. -->
|
||||
|
||||
**Learnings.** <!-- What future-you should know. -->
|
||||
|
||||
**Harvested.** <!-- Wiki pages updated (FAQ, Stolpersteine, …) with
|
||||
links; framework issues opened, if a rule was missing or wrong. -->
|
||||
|
||||
**Open uncertainties.** <!-- Session-handoff answers to: "Which choices
|
||||
did I make that I'm least confident about?" -->
|
||||
|
||||
<!-- After approval: set status: done, move this file to
|
||||
docs/design/done/, run gen_status.py. -->
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0001"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
gitlab_iid: "1"
|
||||
related: []
|
||||
---
|
||||
# MATRIX-03: www.matrix.axion1337.de ist überflüssig
|
||||
|
||||
> Import aus [management#1](https://git.lab/axion1337.chat/management/-/issues/1) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
A-Record `www.matrix.axion1337.de` → `49.13.132.245`, nach IONOS-Default-Muster
|
||||
angelegt. Begründung, warum `www.` bei einer Subdomain überflüssig ist: siehe ZONE-01.
|
||||
**Nicht verifiziert**, ob auf dem Host etwas auf den Namen hört.
|
||||
|
||||
**Nächster Schritt:** prüfen und sonst löschen.
|
||||
|
||||
Quelle: [hosts/matrix.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/matrix.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0002"
|
||||
status: waiting
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
host: game
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
gitlab_iid: "2"
|
||||
related: []
|
||||
---
|
||||
# GAME-01: Host von CFGMON aus nicht erreichbar, 2 Prometheus-Targets down
|
||||
|
||||
> Import aus [management#2](https://git.lab/axion1337.chat/management/-/issues/2) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Zwei Scrape-Targets sind down (`gameserver_cadvisor` 157.90.155.206:8080,
|
||||
`pterodactyl_host_node` :9100, beide `context deadline exceeded`) — bestand schon
|
||||
**vor** dem Monitoring-Rework; `up == 1` in 45 Tagen Retention **nie**.
|
||||
|
||||
**Eingrenzung 2026-08-01 (von CFGMON aus):** Port 80/443 offen und antworten sofort;
|
||||
22/8080/9100 Timeout (nicht refused → Signatur eines Paketfilters davor); ICMP 100 %
|
||||
Verlust; Host ist **nicht** im vSwitch 10.0.0.0/24. Damit ist „Host tot/umgezogen"
|
||||
ausgeschlossen und die **Hetzner-Cloud-Firewall die wahrscheinliche Ursache**;
|
||||
Zusatzbedingung möglich: Exporter binden nur 127.0.0.1.
|
||||
|
||||
**Empfehlung: nicht über die öffentliche IP freigeben**, sondern den Host in den
|
||||
Hetzner-vSwitch aufnehmen (Modell k3s: CFGMON scrapt 10.0.0.2:9100 privat, keine im
|
||||
Internet offenen Exporter-Ports). Danach in `threadnet-operating`
|
||||
`monitoring/prometheus/prometheus.yml` die Targets von der rohen IP auf die private
|
||||
Adresse umstellen.
|
||||
|
||||
⚠️ **Alerting-Silences laufen am 2026-08-04 01:30 UTC ab** (`abedb8a2…` und
|
||||
`0f64aa3c…`); danach melden sich beide `TargetDown`-Alarme alle 4 h zurück. Verlängern:
|
||||
`docker compose exec alertmanager amtool silence expire <id>
|
||||
--alertmanager.url=http://localhost:9093` aus `/opt/threadnet-operating/monitoring`.
|
||||
|
||||
Quelle: [hosts/game.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/game.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0003"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
gitlab_iid: "3"
|
||||
related: []
|
||||
---
|
||||
# GAME-02: www.game.axion1337.de ist überflüssig
|
||||
|
||||
> Import aus [management#3](https://git.lab/axion1337.chat/management/-/issues/3) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
A-Record `www.game.axion1337.de` → `157.90.155.206` nach IONOS-Default-Muster
|
||||
(Begründung siehe ZONE-01). Nicht verifiziert, ob etwas auf den Namen hört — der Host
|
||||
ist von CFGMON aus nicht erreichbar (GAME-01).
|
||||
|
||||
**Nächster Schritt:** prüfen, ob der Name irgendwo verlinkt/konfiguriert ist, sonst
|
||||
A-Record löschen.
|
||||
|
||||
Quelle: [hosts/game.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/game.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0004"
|
||||
status: waiting
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: low
|
||||
host: overmind
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
gitlab_iid: "4"
|
||||
related: []
|
||||
---
|
||||
# OVERMIND-02: e1000e-NIC-Hang — Beobachtung nach EEE-Fix + Firmware-Update
|
||||
|
||||
> Import aus [management#4](https://git.lab/axion1337.chat/management/-/issues/4) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Host-Ausfall 2026-07-31 ~19:15: `e1000e Detected Hardware Unit Hang` auf `eno1`
|
||||
(bekanntes EEE-Problem) — Host lief, war aber netzwerktot. **Fix aktiv:** EEE per
|
||||
`ethtool` aus + persistente udev-Regel (`71-disable-eee-eno1.rules`).
|
||||
**NIC-/BIOS-Firmware 2.4.0.0 → 2.5.2.0 erledigt** (Wartungsfenster 2026-08-01, sorb).
|
||||
|
||||
**Rest = Beobachtung:** Falls der Hang trotz EEE-off + neuer Firmware wiederkehrt,
|
||||
gezielter ASPM-Fix statt globalem Kernel-Parameter. Ohne Wiederauftreten nach ~4 Wochen
|
||||
(Ende August) schließen.
|
||||
|
||||
Volle Zeitleiste: [hosts/overmind.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/overmind.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0005"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "5"
|
||||
related: []
|
||||
---
|
||||
# ZONE-01: IONOS-Default-Records bereinigen (www-Paare, tote Mail-Sätze)
|
||||
|
||||
> Import aus [management#5](https://git.lab/axion1337.chat/management/-/issues/5) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
IONOS legt je Subdomain automatisch `www.`-Paare und komplette Mail-Sätze an
|
||||
(MX, SPF ~all, DKIM-CNAMEs, autodiscover) — auch für Hosts ohne Mail. Ungenutzte
|
||||
Subdomain mit gültigem MX + Softfail-SPF = Spoofing-Vektor; ohne MX weichen Absender
|
||||
per RFC 5321 auf A/AAAA aus. Richtig: explizit „keine Mail" erklären — **Null-MX
|
||||
(RFC 7505), `v=spf1 -all`, `_dmarc p=reject`** — statt ersatzlos löschen.
|
||||
|
||||
**Stand:** `rohana` + `selendis` in Arbeit (sorb setzt direkt um, seit 2026-07-30).
|
||||
Offen: `matrix` (Mail-Satz kann weg — MATRIX-01 hat verifiziert, dass weder Synapse
|
||||
noch MAS Mail versenden), `www.game`/`www.matrix` (GAME-02, MATRIX-03), `ftp` (zeigt
|
||||
auf IONOS-Hosting — Ballast, löschen falls ungenutzt).
|
||||
|
||||
⚠️ Beim SPF-Ändern **bestehenden TXT editieren**, nie zweiten anlegen (PermError).
|
||||
Nach Umsetzung verifizieren: www-Namen lösen nicht mehr auf, genau EIN SPF pro Name,
|
||||
A/AAAA von rohana/selendis unangetastet (Gitea/Grafana weiter per HTTPS erreichbar).
|
||||
|
||||
Detail-Rezepte pro Name (Löschen/Anlegen-Tabellen): Git-Historie von
|
||||
[shared/zone-axion1337.md](https://git.lab/axion1337.chat/management/-/blob/main/shared/zone-axion1337.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
|
||||
---
|
||||
|
||||
## Rezepte pro Name (aus dem Backlogs-Markdown übernommen)
|
||||
|
||||
### rohana.axion1337.de
|
||||
**Löschen:** `A www.rohana`, `AAAA www.rohana`
|
||||
**Anlegen:** MX `rohana` = `.` (Prio 0) · TXT `rohana` = `v=spf1 -all` · TXT `_dmarc.rohana` = `v=DMARC1; p=reject;`
|
||||
**Nicht anfassen:** `A`/`AAAA rohana` — daran hängen Gitea und das Zertifikat.
|
||||
|
||||
### selendis.axion1337.de
|
||||
**Löschen:** `MX mx00/mx01.ionos.de`, `CNAME s1-ionos._domainkey`, `s2-ionos._domainkey`, `s42582890._domainkey`, `CNAME autodiscover.selendis`, `A`/`AAAA www.selendis`
|
||||
**Ändern:** TXT `selendis` von `v=spf1 include:_spf-eu.ionos.com ~all` auf `v=spf1 -all` — **bestehenden Record editieren, keinen zweiten anlegen** (PermError!)
|
||||
**Anlegen:** MX `selendis` = `.` (Prio 0) · TXT `_dmarc.selendis` = `v=DMARC1; p=reject;`
|
||||
**Vorab prüfen:** ob im IONOS-Mail-Bereich Postfach/Weiterleitung für `selendis` existiert (dann entfallen die MX-Änderungen). Falls IONOS `.` als MX-Ziel ablehnt: MX weglassen, TXT reicht.
|
||||
|
||||
### matrix.axion1337.de (entblockt durch MATRIX-01)
|
||||
Gleiches Härtungsmuster wie selendis: kompletten IONOS-Mail-Satz entfernen, Null-MX + `v=spf1 -all` + `_dmarc p=reject`; `autodiscover.matrix` kann weg. (`www.matrix` → #1.)
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0006"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: low
|
||||
area: security
|
||||
gitlab_iid: "6"
|
||||
related: []
|
||||
---
|
||||
# ZONE-02: Apex-DMARC ist p=none und schützt nichts
|
||||
|
||||
> Import aus [management#6](https://git.lab/axion1337.chat/management/-/issues/6) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
`_dmarc.axion1337.de` = `v=DMARC1; p=none;` — reines Monitoring, kein Schutz
|
||||
gegen gefälschte Mail. Zusätzlich fehlt `sp=`: **alle Subdomains erben p=none**, auch
|
||||
künftige. `sp=reject` am Apex wäre der effiziente Hebel und macht die einzelnen
|
||||
`_dmarc`-Records aus ZONE-01 auf Dauer entbehrlich.
|
||||
|
||||
**Reihenfolge wichtig:** erst für jeden real sendenden Namen SPF/DKIM korrekt setzen,
|
||||
dann `sp=reject` — umgekehrt zerlegt es Mailversand unbemerkt. Für den Apex selbst
|
||||
(echte IONOS-Mail): `p=none` → `p=quarantine` → Reports beobachten → `p=reject`.
|
||||
Auffällig: `s1._domainkey.axion1337.de` hatte keinen DKIM-Record, obwohl die
|
||||
Subdomains IONOS-DKIM-CNAMEs haben — beim Härten mitprüfen.
|
||||
|
||||
Quelle: [shared/zone-axion1337.md](https://git.lab/axion1337.chat/management/-/blob/main/shared/zone-axion1337.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0007"
|
||||
status: next
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: high
|
||||
due: 2026-09-28
|
||||
host: cfgmon
|
||||
gitlab_iid: "7"
|
||||
related: []
|
||||
---
|
||||
# CFGMON-01: Zertifikatserneuerung braucht offene Ports — zeitkritisch ab 2026-09-28
|
||||
|
||||
> Import aus [management#7](https://git.lab/axion1337.chat/management/-/issues/7) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Certs für `selendis`/`rohana` laufen am **2026-10-28** ab; Traefik erneuert ab
|
||||
Ende September via TLS-ALPN-01 — braucht **Port 443 offen aus dem ganzen Internet**
|
||||
(LE veröffentlicht keine Validierungs-IPs, Multi-Perspective-Validation). Der
|
||||
Normalzustand der Umgebung (443 auf eigene IP beschränkt) lässt die Erneuerung
|
||||
**still** scheitern → Self-Signed-Default-Cert. IPv6-Pfad ist geprüft frei
|
||||
(`::/0` separat in der Hetzner-Firewall-Regel; `0.0.0.0/0` deckt IPv6 NICHT ab) —
|
||||
die September-Erneuerung ist aber der **erste** Lauf, der IPv6 überhaupt versucht.
|
||||
|
||||
**Entscheidung nötig:**
|
||||
- **A — Ports offen lassen** bzw. zur Erneuerung öffnen (Kalendereintrag Mitte
|
||||
September, nicht aufs Ablaufdatum!)
|
||||
- **B — auf DNS-01 umstellen (empfohlen):** TXT-Validierung, kein offener Port,
|
||||
ermöglicht Wildcards. Braucht IONOS-API-Token als Traefik-Secret; Voraussetzung
|
||||
(versionierter Traefik-Stack) ist seit CFGMON-02 erfüllt.
|
||||
|
||||
Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/cfgmon.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0008"
|
||||
status: waiting
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
host: cfgmon
|
||||
area: security
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
gitlab_iid: "8"
|
||||
related: []
|
||||
---
|
||||
# CFGMON-03: Prometheus-Remote-Write und Loki öffentlich ohne Auth — Weg A, nachgelagerte Prüfung
|
||||
|
||||
> Import aus [management#8](https://git.lab/axion1337.chat/management/-/issues/8) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Prometheus 9090 (`--web.enable-remote-write-receiver`) und Loki 3100 sind
|
||||
öffentlich ohne Auth — Fremde könnten Metriken einspeisen und Daten/Logs auslesen.
|
||||
Absender-Inventur: k3s/Matrix pusht längst privat (10.0.0.3); öffentlich bräuchte die
|
||||
Ports nur der GAME-Host (→ GAME-01).
|
||||
|
||||
**Weg A beschlossen (sorb 2026-08-01):** Hetzner-Cloud-Firewall — 9090/3100 nur für
|
||||
bekannte Absender. **Nachgelagerte Prüfung nötig:** Beim Baseline-Check vom Mac waren
|
||||
9090/3100 bereits zu, OHNE dass der Console-Klick gemacht war — die reale
|
||||
Firewall-Lage weicht vom Backlog-Bild ab. Vor dem Abhaken **gemeinsam in die
|
||||
Hetzner-Console schauen**: welche Regeln existieren wirklich, und läuft der GAME-Push
|
||||
(nach GAME-01) noch durch? Weg B (GAME in den vSwitch, Ports ganz zu) bleibt die
|
||||
saubere Endstufe; Weg C (BasicAuth via Traefik) verworfen.
|
||||
|
||||
Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/cfgmon.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0009"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
host: cfgmon
|
||||
gitlab_iid: "9"
|
||||
related: []
|
||||
---
|
||||
# CFGMON-04: Grafana-Admin-Credentials aus .env gelten nicht für die HTTP-API
|
||||
|
||||
> Import aus [management#9](https://git.lab/axion1337.chat/management/-/issues/9) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
`GF_SECURITY_ADMIN_USER/PASSWORD` greifen nur beim allerersten Start mit leerem
|
||||
Volume; der Live-Admin wurde später in der UI geändert — die `.env` sieht aus wie die
|
||||
Quelle der Wahrheit, ist es aber nicht. Verifikation läuft deshalb über `grafana.db`.
|
||||
|
||||
**Nächster Schritt:** Service-Account mit API-Token für Verifikationszwecke anlegen
|
||||
(sauberer als das echte Admin-Passwort in die `.env` nachzuziehen).
|
||||
|
||||
Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/cfgmon.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0010"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
host: cfgmon
|
||||
area: security
|
||||
gitlab_iid: "10"
|
||||
related: []
|
||||
---
|
||||
# CFGMON-09: Gitea-Backups off-host (Borg/Storage Box) — Backup-Cron ist DEAKTIVIERT
|
||||
|
||||
> Import aus [management#10](https://git.lab/axion1337.chat/management/-/issues/10) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
⚠️ **Seit 2026-07-30 laufen KEINE Gitea-Backups** — der nächtliche Cron ist
|
||||
auskommentiert (Crontab `rantanplan`), letzter Stand
|
||||
`/opt/backup/gitea-dump-2026-07-30.tar.gz`. Beim Erledigen/Verwerfen dieses Punkts
|
||||
den Cron wieder aktivieren.
|
||||
|
||||
**Plan: eigenes Borg-Repo auf einer Hetzner Storage Box** (spricht Borg nativ über
|
||||
SSH Port 23). Dump **unkomprimiert** an Borg geben (gzip im Script entfällt, sonst
|
||||
greift Dedup nicht); Retention via `borg prune` (7d/4w/6m); optional Sub-Account.
|
||||
Kontext: Platte 73 % voll, Script rotiert auf genau einen Stand, Off-host-Kopie
|
||||
fehlt komplett — bei Verlust des Hosts wäre Gitea (inkl. Mirror-Kopien) weg.
|
||||
|
||||
**Voraussetzungen (User):** Storage-Box/Sub-Account im Robot anlegen; Host hat keinen
|
||||
SSH-Key → generieren und Public Key in der Storage Box hinterlegen.
|
||||
|
||||
Quelle: [hosts/cfgmon.md](https://git.lab/axion1337.chat/management/-/blob/main/hosts/cfgmon.md)
|
||||
|
||||
---
|
||||
*Übernommen aus dem Backlogs-Markdown beim Framework-Umbau 2026-08-01 (voller Wortlaut: Git-Historie der Datei).*
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0014"
|
||||
status: waiting
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
host: cfgmon
|
||||
area: security
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
gitlab_iid: "14"
|
||||
related: []
|
||||
---
|
||||
# CFGMON-14: Root-Zugang über die docker-Gruppe umgeht sudo und hinterlässt keine Spur
|
||||
|
||||
> Import aus [management#14](https://git.lab/axion1337.chat/management/-/issues/14) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Aus dem [CFGMON-AAR](https://git.lab/axion1337.chat/management/-/blob/main/verfahren/aar/2026-08-01-labnet02-cfgmon.md) (Befund 3, MEDIUM), Entscheidung liegt bei sorb.
|
||||
|
||||
`sudo` ist aus einer Agenten-Session nicht bedienbar (kein TTY: *„a terminal is required to read the password"*). Die LABNET-02-Schritte liefen deshalb über die **docker-Gruppenmitgliedschaft** des Kontos `rantanplan` — privilegierter Container plus `nsenter` in die Host-Namespaces. Das ist **root-äquivalent**.
|
||||
|
||||
**Konsequenz:** Die sudo-Passwortabfrage ist für dieses Konto keine wirksame Sicherheitsgrenze, und dieser Weg hinterlässt **keinen Eintrag in `auth.log`**. Auf Linux ist das normales Verhalten der docker-Gruppe und kein Konfigurationsfehler — aber es sollte eine bewusste Entscheidung sein.
|
||||
|
||||
**Optionen:**
|
||||
- **A — so lassen**, aber dokumentieren (dann ist „sudo mit Passwort" auf diesem Host explizit kein Kontrollmechanismus mehr)
|
||||
- **B — Konto aus der docker-Gruppe nehmen** und Docker-Zugriff über eine gezielte sudo-Regel führen (auditierbar, aber Agenten-Sessions brauchen dann einen anderen Weg)
|
||||
- **C — getrenntes Konto** für Agenten-Sessions mit definierter, protokollierter Rechteerhöhung
|
||||
|
||||
Vor einer Entscheidung zu klären: Welche anderen Konten sind in der docker-Gruppe, und gilt dasselbe auf MATRIX?
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0015"
|
||||
status: next
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: medium
|
||||
due: 2026-08-31
|
||||
host: cfgmon
|
||||
area: security
|
||||
gitlab_iid: "15"
|
||||
related: []
|
||||
---
|
||||
# CFGMON-15: Token-Hygiene — Einmal-Tokens der LABNET-02-Nacht widerrufen
|
||||
|
||||
> Import aus [management#15](https://git.lab/axion1337.chat/management/-/issues/15) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Gemeldet von der CFGMON-Session am Ende der LABNET-02-Nacht.
|
||||
|
||||
Während der Arbeit entstanden **vier Einmal-Tokens** für Issue-Kommentare/Pushes, dazu existiert noch das **erste git.lab-Token** aus dem Erstzugang. Alle sind nach Abschluss von LABNET-02 funktionslos.
|
||||
|
||||
**Zu tun:** Bestand aufnehmen (Gitea-Access-Tokens + GitLab-PATs), nicht mehr benötigte widerrufen, verbleibende mit Ablaufdatum und sprechendem Namen versehen.
|
||||
|
||||
⚠️ **Randbedingung aus der Mirror-Diskussion:** Welcher Token in den Push-Mirrors der fünf gespiegelten Repos hinterlegt ist, ist derzeit **nicht rekonstruierbar** (die API maskiert ihn, in keiner Session dokumentiert). Ein Widerruf kann deshalb still einen Mirror brechen. Vor der Rotation: entweder die Mirror-Credentials bewusst neu setzen, oder nach dem Widerruf jeden Mirror-Status einmal prüfen (`GET /projects/<id>/remote_mirrors` → `last_error`).
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0018"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "18"
|
||||
related: []
|
||||
---
|
||||
# DOC-01: Wiki-Rollout abschließen — CI-Freigaben, Zeitplan, Dokploy-Stack, wiki.lab
|
||||
|
||||
> Import aus [management#18](https://git.lab/axion1337.chat/management/-/issues/18) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Das Wiki-Repo [`homelab/wiki`](https://git.lab/homelab/wiki) steht ([ADR-0006](https://git.lab/axion1337.chat/management/-/blob/main/decisions/0006-wikis-konsolidieren-docusaurus.md)), der Bau ist lokal verifiziert (46 Seiten, 3,6 MB). Zum Betrieb fehlen noch Schritte, die Rechte oder die Dokploy-Oberfläche brauchen:
|
||||
|
||||
1. **Job-Token-Freigaben** — in jedem Quell-Repo unter *Settings → CI/CD → Job token permissions* das Projekt `homelab/wiki` erlauben: `axion1337.chat/axion1337.chat-gitops` (fürs Wiki-Repo!), `homelab/docs`, `axion1337.chat/management`. Ohne das schlägt `sync-sources.sh` in der CI fehl.
|
||||
2. **Erste Pipeline** in `homelab/wiki` laufen lassen (manuell) und prüfen, dass `registry.git.lab/homelab/wiki:latest` entsteht.
|
||||
3. **Pipeline-Zeitplan** anlegen (*Build → Pipeline schedules*, Vorschlag: täglich nachts) — das ist der eigentliche Aktualisierungsmechanismus.
|
||||
4. **Dokploy-Stack** aus `docker-compose.yml`, Domain `wiki.lab` → Port 80, Zertifikat von der aXionLabs-CA.
|
||||
5. **Lab-DNS**: `wiki.lab` → `10.58.73.17`.
|
||||
6. Danach: Link auf wiki.lab in den README der Quell-Repos gegenprüfen (gitops ist erledigt).
|
||||
|
||||
**Verifikation:** `https://wiki.lab` zeigt die drei Bereiche; eine Änderung in einer Quelle ist nach dem nächsten geplanten Lauf sichtbar.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0019"
|
||||
status: open
|
||||
created: 2026-08-01
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "19"
|
||||
related: []
|
||||
---
|
||||
# DOC-02: Veralteten `wiki`-Branch im gitops-Repo entfernen?
|
||||
|
||||
> Import aus [management#19](https://git.lab/axion1337.chat/management/-/issues/19) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Der Branch `wiki` im Repo `axion1337.chat-gitops` (Commit `0ff598e8`, **2026-05-14**) ist ein Abzug des damaligen `docs/`-Verzeichnisses — **nicht** das gepflegte Wiki (das lag auf Gitea und liegt seit 2026-08-02 im GitLab-Wiki, siehe [ADR-0006](https://git.lab/axion1337.chat/management/-/blob/main/decisions/0006-wikis-konsolidieren-docusaurus.md)).
|
||||
|
||||
Er ist damit eine Fehlerquelle: Wer ihn findet, hält ihn für Dokumentation und liest drei Monate alte Stände.
|
||||
|
||||
**Aktuell** ist er in README und CLAUDE.md ausdrücklich als überholt markiert — das ist die minimale, nicht-destruktive Maßnahme.
|
||||
|
||||
**Zu entscheiden:** löschen (sauberer, die Historie bleibt über den Mirror und die Reflogs erreichbar) oder als Archiv behalten. Wenn löschen: erst prüfen, ob der Branch Inhalte enthält, die es **nirgends sonst** gibt — `docs/oldwiki/` und `docs/setup/` sahen im Vergleich danach aus.
|
||||
|
||||
**Nicht ungefragt gelöscht**, weil ein Branch-Löschen im gespiegelten Repo auch den Mirror trifft.
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0020"
|
||||
status: next
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: medium
|
||||
due: 2026-08-31
|
||||
area: infrastructure
|
||||
gitlab_iid: "20"
|
||||
related: []
|
||||
---
|
||||
# DOC-03: Wiki-Oberfläche entscheiden — Docusaurus oder BookStack
|
||||
|
||||
> Import aus [management#20](https://git.lab/axion1337.chat/management/-/issues/20) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Zwei Varianten stehen nebeneinander, damit an echten Inhalten entschieden wird statt am Reißbrett ([ADR-0007](https://git.lab/axion1337.chat/management/-/blob/main/decisions/0007-wiki-oberflaeche-docusaurus-vs-bookstack.md)):
|
||||
|
||||
| Variante | Stand | Repo |
|
||||
|---|---|---|
|
||||
| **Docusaurus** | läuft unter `axionwiki.lab` | [homelab/wiki](https://git.lab/homelab/wiki) |
|
||||
| **BookStack** | Stack fertig, noch nicht deployt | [homelab/wiki-bookstack](https://git.lab/homelab/wiki-bookstack) |
|
||||
|
||||
**Die eigentliche Frage** ist nicht das Werkzeug, sondern: Soll Dokumentation künftig **im Repo** entstehen (Commit, Review, Git-Historie) oder **im Browser** (WYSIWYG, Rechte je Buch, eingebaute Suche)? Mit BookStack entsteht eine **zweite Quelle der Wahrheit** neben git.lab — das kann richtig sein, muss aber bewusst entschieden werden.
|
||||
|
||||
**Zum Ausprobieren:** BookStack deployen (Anleitung im README, drei Pflicht-Secrets), zwei bis drei Seiten anlegen, beide Oberflächen im Alltag vergleichen. Themes liegen in beiden Wunschfarben bei (Gruvbox Dark und Sunset Boulevard, farbgleich zu den Element-Themes), damit der Vergleich nicht an der Optik hängt.
|
||||
|
||||
**Verfallsdatum setzen:** Doppelter Betrieb ist nur als Vergleich vertretbar. Vorschlag: Entscheidung im ersten Refinement (#17), spätestens Ende August — danach wird die Verliererseite abgeräumt, nicht „für später" behalten.
|
||||
|
||||
⚠️ Falls BookStack gewinnt: **Backup wird Pflicht** (Datenbank!), Anschluss an das Verfahren aus CFGMON-09.
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0021"
|
||||
status: waiting
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
gitlab_iid: "21"
|
||||
related: []
|
||||
---
|
||||
# OVERMIND-03: Windows-Build-VM verschwindet — CI kann sie nur starten, nicht anlegen
|
||||
|
||||
> Import aus [management#21](https://git.lab/axion1337.chat/management/-/issues/21) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Der CI-Job `start_windows_vm` macht ausschließlich `docker start windows-runner`. Existiert der Container nicht, scheitert er mit `No such container: windows-runner` — so geschehen am 2026-08-02 (Job 498), nachdem der Container zwischenzeitlich verschwunden war (vermutlich durch einen Dokploy-Redeploy oder den Host-Neustart; nicht verifiziert).
|
||||
|
||||
sorb hat ihn manuell neu gestartet, danach lief der Build. Der Fall wiederholt sich aber, sobald der Stack erneut angefasst wird.
|
||||
|
||||
**Optionen:**
|
||||
- **A** — Job robuster machen: bei fehlendem Container den Dokploy-Stack `windows-runner` per API neu deployen statt nur zu starten.
|
||||
- **B** — Container über `restart: unless-stopped` dauerhaft halten. ⚠️ Widerspricht dem On-demand-Prinzip (die VM belegt 8 GB) und war eine bewusste Entscheidung.
|
||||
- **C** — so lassen, aber die Fehlermeldung im Job um den Hinweis „Stack in Dokploy neu deployen" ergänzen (billigste Variante).
|
||||
|
||||
Empfehlung: **C jetzt, A wenn es ein drittes Mal passiert.**
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0022"
|
||||
status: open
|
||||
created: 2026-08-02
|
||||
milestone: M4
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "22"
|
||||
related: []
|
||||
---
|
||||
# BUILD-01: macOS-Client reproduzierbar bauen — aktuell nur manuell auf sorbs Mac
|
||||
|
||||
> Import aus [management#22](https://git.lab/axion1337.chat/management/-/issues/22) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Der macOS-Client wurde am 2026-08-02 erstmals gebaut (Release [desktop-1.12.17-themes](https://git.lab/axion1337.chat/ThreadNet-Web/-/releases/desktop-1.12.17-themes)), aber **von Hand auf sorbs Mac** und mit zwei Umgehungen. Reproduzierbar ist das so nicht.
|
||||
|
||||
## Was gemacht werden musste
|
||||
|
||||
| Hürde | Umgehung | Dauerhaft? |
|
||||
|---|---|---|
|
||||
| `Can't find rustc` (native Module sqlcipher/seshat) | rustup installiert, nach dem Build wieder entfernt | ❌ bei jedem Build neu |
|
||||
| `Failed to check actool version. Is Xcode 26 or higher installed?` beim **DMG** | DMG mit `hdiutil` statt electron-builder gebaut | ⚠️ funktioniert, aber ohne Installer-Layout |
|
||||
| Code-Signing | unsigniert, `CSC_IDENTITY_AUTO_DISCOVERY=false` | ❌ Nutzer müssen `xattr -dr com.apple.quarantine` ausführen |
|
||||
|
||||
Das **ZIP** baut electron-builder 26 problemlos; nur das DMG-Target verlangt `actool` aus dem vollen Xcode (~10 GB, nur über den App Store mit Apple-ID).
|
||||
|
||||
## Optionen
|
||||
|
||||
- **A — so lassen**: macOS bleibt ein manueller Build vor jedem Release. Billig, aber jedes Mal dieselben Handgriffe und leicht zu vergessen.
|
||||
- **B — Mac-Runner im Lab**: braucht Apple-Hardware, volles Xcode und einen GitLab-Runner darauf. Löst auch das DMG-Problem.
|
||||
- **C — DMG dauerhaft per `hdiutil`** in einem Skript im Repo: nimmt electron-builder das DMG ab, funktioniert ohne Xcode. Signing bleibt offen.
|
||||
|
||||
Empfehlung: **C jetzt** (kostet eine Stunde, macht den Build ohne Xcode vollständig), **B**, wenn macOS ein regelmäßiges Ziel wird.
|
||||
|
||||
## Hängt zusammen mit
|
||||
|
||||
- ThreadNet-Web#6 (Signing/Notarisierung) — ohne Signatur bleibt die Gatekeeper-Hürde für jeden Nutzer.
|
||||
- ThreadNet-Web#10 (Rebrand) — bereits teilweise umgesetzt: `apps/desktop/axion1337/build.json` (Commit `c8d4587`) macht aus `Element.app` eine `ThreadNet.app` mit eigenem Icon.
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0023"
|
||||
status: open
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "23"
|
||||
related: []
|
||||
---
|
||||
# DOC-04: Navbar-Logo im Docusaurus-Wiki wird ausgeliefert, ist aber nicht sichtbar
|
||||
|
||||
> Import aus [management#23](https://git.lab/axion1337.chat/management/-/issues/23) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Stand aus dem [AAR](https://git.lab/axion1337.chat/management/-/blob/main/verfahren/aar/2026-08-02-wiki-und-desktop-clients.md) — bisher nur dort notiert, deshalb jetzt als Issue.
|
||||
|
||||
Auf `axionwiki.lab` erscheint links neben dem Titel kein sichtbares Logo, obwohl alle Bestandteile nachweislich korrekt ausgeliefert werden:
|
||||
|
||||
- **HTML**: `<div class="navbar__logo">` enthält beide Theme-Varianten, beide mit `src="/img/logo.png"`
|
||||
- **CSS**: `.navbar__logo{height:2.6rem}` und `.navbar__logo img{height:100%;width:auto}` stehen im ausgelieferten Stylesheet
|
||||
- **Bild**: `/img/logo.png` liefert HTTP 200, 183 × 128 px, Motiv füllt die Fläche vollständig
|
||||
|
||||
Da alle drei Teile stimmen, hilft nur ein Blick in die Entwicklerkonsole: Wird das Bild geladen (Network-Tab) oder scheitert es? Und welche berechnete Höhe hat das `img`-Element tatsächlich (Elements → Computed)? Denkbar ist, dass eine Docusaurus-eigene Regel mit höherer Spezifität die Höhe auf 0 oder 2rem zwingt, oder dass die Theme-Umschaltung beide Varianten ausblendet.
|
||||
|
||||
**Kein Blocker** — das Wiki funktioniert, es ist Kosmetik. Erst angehen, wenn jemand ohnehin am Wiki arbeitet.
|
||||
|
||||
Verwandt: Das Favicon war ein eigener Fall (Wurzelpfad lieferte HTML statt Icon) und ist gelöst.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0024"
|
||||
status: open
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "24"
|
||||
related: []
|
||||
---
|
||||
# Wiki-Hostname klären: wiki.lab oder axionwiki.lab?
|
||||
|
||||
> Import aus [management#24](https://git.lab/axion1337.chat/management/-/issues/24) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0025"
|
||||
status: waiting
|
||||
created: 2026-08-01
|
||||
milestone: M1
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
gitlab_iid: "25"
|
||||
related: []
|
||||
---
|
||||
# Deploy-Übergabe: CVE-Alarme aggregiert + Receiver-Robustheit (gitops#51, ff87cb2)
|
||||
|
||||
> Import aus [management#25](https://git.lab/axion1337.chat/management/-/issues/25) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
### Stand
|
||||
threadnet-operating `ff87cb2` (git.lab; Gitea-Mirror folgt — auf CFGMON vorher `git fetch && git reset --hard origin/main`, der gerettete Kollegen-Commit heißt jetzt `0bd77e2`)
|
||||
|
||||
### Testtiefe
|
||||
ungetestet — Lints grün (promtool 9 Regeln, amtool, py_compile), kein Laufzeittest
|
||||
|
||||
### Mengengerüst
|
||||
Erwartete Matrix-Nachrichten beim Scharfschalten: **eine pro Image mit CRITICAL-Funden**, obere Schranke 29 (gemessen an 14 Images hatten die meisten CRITICALs → realistisch ~15–25 Nachrichten, je 1 s gedrosselt ≈ unter 30 s). HIGH-Alarme folgen frühestens nach 24 h (`for: 24h`), gleiche Schranke. Danach nur Deltas (neue Images/Severity-Wechsel) und ✅-Edits. Kein Pro-CVE-Verkehr mehr: Regeln sind `count by (target, target_type, host)`, die ~1200 Einzelserien erzeugen keine Alarme mehr (bleiben aber als `trivy_vuln_info` fürs Dashboard).
|
||||
|
||||
### Vollständiges Deploy-Kommando
|
||||
```
|
||||
cd /opt/threadnet-operating && git fetch && git reset --hard origin/main && cd monitoring && docker compose up -d --force-recreate matrix-alerts && docker compose exec prometheus kill -HUP 1 && docker compose exec alertmanager kill -HUP 1
|
||||
```
|
||||
(reset --hard wegen Hash-Wechsel 2b715ca→0bd77e2; force-recreate lädt das ro-gemountete Receiver-Skript neu; HUPs laden Regeln/Route ohne Neustart)
|
||||
|
||||
### Woran erkennt man, dass es wirklich greift
|
||||
1. `docker compose logs matrix-alerts --since 5m` — keine Fehler, keine 502-Schleife
|
||||
2. Security-Raum: binnen ~2 min (group_wait 1m) trudeln die aggregierten 🔴-Nachrichten „Image X: N CRITICAL-CVEs" einzeln im Sekundentakt ein — **gezählt ≤ 29**, keine Pro-CVE-Flut
|
||||
3. `curl -s localhost:9090/api/v1/rules | grep -c TrivyCriticalVulns` → 1 (neue Regel geladen)
|
||||
4. Alertmanager-Retry-Probe: Log darf nach Abschluss der Zustellung keine wiederholten identischen Batches zeigen
|
||||
|
||||
### Außenwirkung und Not-Aus
|
||||
Außenwirkung: nur der Security-Matrix-Raum (interner Kreis). **Not-Aus:** in `monitoring/alertmanager/alertmanager.yml` die Route `room="security"` wieder auf einen `"null"`-Receiver biegen (Muster steht in der Git-Historie, Commit `0bd77e2`) + `docker compose exec alertmanager kill -HUP 1` — wirkt sofort, Pipeline läuft weiter.
|
||||
|
||||
### Rollback
|
||||
`git revert ff87cb2` (ein Commit, betrifft nur alerts.yml/alertmanager.yml/matrix-alerts.py) + dieselben drei Kommandos wie beim Deploy. State-Datei ist abwärtskompatibel (neues Format kapselt das alte unter `alerts`).
|
||||
|
||||
### Bewusst offen gelassen
|
||||
- Grafana-Dashboard als **Matrix-Widget** im Security-Raum (Wunsch sorb): braucht `allow_embedding` in Grafana + Lese-Zugang ohne Login — eigener Punkt, nicht Teil dieses Deploys
|
||||
- gitops#52 (Inode-Falle) unberührt
|
||||
- Erste HIGH-Welle kommt erst nach 24 h — bewusst, keine Fehlfunktion
|
||||
|
||||
---
|
||||
*Migriert aus Gitea `sorb/management#1` (Gitea-Tracker stillgelegt, ADR-0002) — dort erstellt am 2026-08-01 von sorb.*
|
||||
<!-- gitea-migration: sorb/management#1 -->
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0027"
|
||||
status: waiting
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
wartegrund: Grund im GitLab-Verlauf benannt (Import 2026-08-11); im nächsten Refinement präzisieren
|
||||
gitlab_iid: "27"
|
||||
related: []
|
||||
---
|
||||
# AUDIT-01: Acht Widersprüche aus dem LABNET-02-Nachlauf (Selbst-Audit CFGMON-Session)
|
||||
|
||||
> Import aus [management#27](https://git.lab/axion1337.chat/management/-/issues/27) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Selbst-Audit der CFGMON-Session (2026-08-02) auf sorbs Bitte: eigene Arbeit gegen `CLAUDE.md`, ADR-0005 und die Verfahren geprüft. **Regel dieses Issues: Widersprüche werden dokumentiert und referenziert, nicht still aufgelöst.** Auflösung einzeln oder gesammelt im Struktur-Workshop (#17). Alle Messungen von heute sind als solche gekennzeichnet.
|
||||
|
||||
## W1 — ADR-0004 (as-built) vs. tatsächliche Split-DNS-Konfiguration
|
||||
|
||||
ADR-0004, CFGMON-Zeile: *„Split-DNS nur `~lab` → 10.58.73.1"*. **Real** (seit 2026-08-01 spätabends, auf sorbs Ansage, `/etc/wireguard/lab.conf` + CFGMON-AAR Nachtrag 2): **vier Zonen** — `~lab`, `~lab.de`, `~axion1337.de`, `~axionlabs.de`. Das ADR beschreibt den As-built-Stand also unvollständig. Brisanz: `~axion1337.de` über den Lab-Resolver betrifft auch `rohana.axion1337.de` (Gitea-Mirror!) — löst der Lab-DNS die Zone anders auf als öffentlich, ändert sich unbemerkt der Pfad zum Mirror. **Auflösung:** ADR nachführen *oder* Zonen auf `~lab` zurückbauen — Entscheidung sorb.
|
||||
|
||||
## W2 — dokumentierte Bootstrap-Routen sind seit der Einzäunung nicht reproduzierbar
|
||||
|
||||
CFGMON-AAR Nachtrag 2 dokumentiert die CA-Verifikation über `ca.axionlabs.de:666` (step-ca, health + roots.pem, Fingerprint-Abgleich). **Messung heute von CFGMON:** `:666` = Timeout (Einzäunung greift, erwartungsgemäß), `git.lab:443` = HTTP 302 ✓, **ICMP zu `10.58.73.17` = 100 % Verlust**. Konsequenzen: (a) Die im AAR beschriebene Verifikationsroute funktioniert nicht mehr — ein künftiger Truststore-Neuaufbau bräuchte eine bewusste Firewall-Ausnahme (**ADR-Pflicht** laut CLAUDE.md). (b) Erreichbarkeits-Checks von CFGMON müssen per **HTTPS statt ping** laufen — alle bisherigen Runbook-Gewohnheiten (`ping 10.58.73.17`) schlagen fehl, obwohl alles gesund ist. Fehldiagnose-Falle für die nächste Session.
|
||||
|
||||
## W3 — `hosts/cfgmon.md` widerspricht sich selbst und der Realität
|
||||
|
||||
Die Dienste-Tabelle (Zeile 27) listet `runner | gitea/act_runner:0.6.1` als laufenden Container — die **eigene Historie** derselben Datei (CFGMON-11-Abschnitt) meldet ihn als am 2026-07-31 restlos entfernt. „Stand: 2026-07-30" deckt zudem nicht: WireGuard-Tunnel (`wg-quick@lab` + systemd-Drop-in `10-after-docker.conf`), aXionLabs-Root-CA im Truststore, git.lab-Zugang via `~/.netrc`. Wenn `hosts/` „Bestand + Historie" ist (CLAUDE.md), ist der Bestand-Teil veraltet; wenn er eingefroren sein soll, fehlt die Kennzeichnung. **Nicht von mir korrigiert** — erst klären, was „Bestand" hier heißen soll.
|
||||
|
||||
## W4 — Schließung von #16 vs. Inhalt von #16 und CLAUDE.md-Secrets-Regel
|
||||
|
||||
Der Schlusskommentar von #16 nennt „die **drei** hier gesammelten Nacharbeiten (Feinschliff)". Das Issue enthielt **fünf** Punkte plus einen Korrektur-Kommentar der CFGMON-Session. Still mitgeschlossen wurden: **Punkt 4** (Repo-Zuhause für `lab.conf`, systemd-Drop-in, Root-CA — Reproduzierbarkeit) und **Punkt 5** (Schlüsselrotation). Bei Punkt 5 kollidiert die Schließung mit der Secrets-Regel der CLAUDE.md (*„anzeigen = Exposure = Rotation"*): Der aktive WG-Private-Key und beide git.lab-PATs liefen im Klartext durch den Chat bzw. das buffer-Repo. Das buffer-Repo ist vernichtet ✓, aber Chat-/Session-Transkripte existieren weiter. Nach der Regel ist die Rotation nicht optional — auch mein eigener Kommentar in #15 („regulär rotieren") war daran gemessen **zu lasch**. **Auflösung:** entweder sorb bestätigt die Schließung ausdrücklich für alle fünf Punkte (dann ist die Secrets-Regel für diesen Fall bewusst ausgesetzt → ADR-Pflicht für die Ausnahme), oder Punkte 4+5 werden als eigenes Issue reaktiviert.
|
||||
|
||||
## W5 — Secrets-Regel vs. gelebte Bootstrap-Praxis der LABNET-02-Nacht
|
||||
|
||||
CLAUDE.md: *„Token-/Secret-Werte niemals […] in Dateien echoen; echte Credentials tippt/legt sorb selbst an; Sessions referenzieren sie nur über Dateipfade."* **Praxis:** Die CFGMON-Session (ich) hat beide PATs selbst in `~/.netrc` geschrieben und den WG-Private-Key nach `/etc/wireguard/` — es gab schlicht keinen anderen Übergabekanal auf einen headless Host. Der Widerspruch ist strukturell, nicht böswillig: Die Regel kennt den Fall „sorb kann die Datei auf dem Zielhost nicht selbst anlegen" nicht. **Auflösung:** Bootstrap-Klausel in die Regel (erlaubt, aber Exposure gilt ⇒ Rotationspflicht + dokumentieren wo), oder Verfahren definieren (z. B. sorb legt per SSH selbst ab, Session referenziert Pfad).
|
||||
|
||||
## W6 — AAR-Vorlage vs. Nachtrag-Praxis
|
||||
|
||||
`verfahren/aar-vorlage.md`: fünf Abschnitte, Gebot „Kurz", Befund-Status mit Issue-Referenz („notiert · Issue"). Der CFGMON-AAR hat inzwischen **acht Abschnitte** (drei Nachträge) und eine Befunde-Tabelle **ohne** Issue-Referenzen (Befund 3 → heute #14; die Issues entstanden erst nach dem AAR). Die Nachtrag-Mechanik — die sich zweimal bewährt hat (Reboot-Korrektur, Auflösung) — ist **nirgends im Verfahren definiert**. **Auflösung:** Vorlage um eine Nachtrag-Regel ergänzen (append-only, datiert, Fundstellen verweisen auf den Nachtrag) oder Nachträge verbieten und Folge-AARs verlangen. Die Befunde-Tabellen der beiden LABNET-02-AARs könnten danach um Issue-Refs ergänzt werden (reine Vervollständigung).
|
||||
|
||||
## W7 — Commit-Autorschaft: drei Identitäten, keine Konvention
|
||||
|
||||
Im management-Repo committen Agenten-Sessions unter drei Identitäten: `sorb <gamemaster@axion1337.de>` ohne Agent-Kennzeichnung (CFGMON-Session: `e8e1b36`, `28cd06c`, `001f59f`; auch `b647645` der Mac-Session), und seit heute `Thore Cimbal <cfx@riot.8shield.net>` **mit** `Co-Authored-By: Claude`-Trailer (`09bdd94`, `ae982cd`, …). Das Kanonisierungs-Verfahren betont Autorschafts-Erhalt als Wert — der ist wenig wert, wenn dieselbe Person/verschiedene Agenten unter wechselnden Identitäten schreiben. Eigenes Versäumnis der CFGMON-Session eingeschlossen: der Claude-Trailer fehlt bei meinen Commits. **Auflösung:** eine Zeile in CLAUDE.md — welcher Author-Name, welche E-Mail, Trailer ja/nein.
|
||||
|
||||
## W8 — UDM-SSH: aktivierte Reständerung ohne Doku
|
||||
|
||||
Für die Fehlersuche wurde SSH auf der UDM aktiviert (`root@10.58.73.1`, eigenes Passwort). Der Lab-AAR erwähnt es nicht, kein Issue trägt es, Status vermutlich „noch an". Root-Shell-Zugang auf dem zentralen Gateway ist ein sicherheitsrelevanter Dauerzustand, wenn er bleibt. **Auflösung:** deaktivieren oder bewusst belassen und als Bestand dokumentieren (analog zur docker-Gruppen-Entscheidung in #14).
|
||||
|
||||
---
|
||||
|
||||
**Konform befunden** (der Vollständigkeit halber): AAR-Pflicht nach Deploy mit Übergabe ✓ (beide AARs), Kanonisierungs-Weg statt Gitea-Push ✓ (alle vier CFGMON-Commits über git.lab, Mirror verifiziert), Redlichkeits-Regeln ✓ (Verifiziert/Vermutet getrennt, eigene Fehlannahme per Nachtrag korrigiert statt geglättet), chirurgische Config-Edits ✓ (sed + `wg-quick strip`-Validierung), Übergabe-Ausnahme auf Gitea während der Nacht ✓ (durch ADR-0002 gedeckt, seit heute per #13 migriert und zurückgebaut).
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0028"
|
||||
status: open
|
||||
created: 2026-08-02
|
||||
milestone: M2
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "28"
|
||||
related: []
|
||||
---
|
||||
# MIRROR-01: Ein Ausfall der Push-Mirrors bleibt unbemerkt — Produktion friert still ein
|
||||
|
||||
> Import aus [management#28](https://git.lab/axion1337.chat/management/-/issues/28) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Der Push-Mirror ist der **einzige** Weg von git.lab in die Produktion: Flux zieht ausschließlich aus Gitea ([ADR-0001](https://git.lab/axion1337.chat/management/-/blob/main/decisions/0001-gitlab-kanonisch-push-mirror.md)). Fällt er aus, passiert nichts Lautes — Flux reconciled weiter den zuletzt gespiegelten Stand. Die Produktion wirkt gesund und ist eingefroren.
|
||||
|
||||
**Kein Alarm, keine rote Pipeline, kein Log, das jemand liest.** Auffallen würde es erst, wenn sich jemand wundert, warum ein Deploy „nicht ankommt".
|
||||
|
||||
## Warum das jetzt zählt
|
||||
|
||||
Aus [#15](https://git.lab/axion1337.chat/management/-/issues/15): **Welches Credential in den Mirrors hinterlegt ist, ist nicht rekonstruierbar** — die API maskiert es, keine Session hat es dokumentiert. Wir wissen also nicht, ob es ein Ablaufdatum hat. Läuft es ab, tritt genau der stille Fall oben ein.
|
||||
|
||||
Stand 2026-08-02 laufen alle geprüften Mirrors fehlerfrei (`update_status: finished`, `last_error: —`) — das ist eine Momentaufnahme, keine Zusicherung.
|
||||
|
||||
## Was zu tun ist
|
||||
|
||||
Ein Check auf den Mirror-Status der sechs gespiegelten Repos der Gruppe `axion1337.chat`:
|
||||
|
||||
```bash
|
||||
curl -sS -H "PRIVATE-TOKEN: $TOKEN" \
|
||||
"https://git.lab/api/v4/projects/<id>/remote_mirrors"
|
||||
# relevant: .update_status != "finished" oder .last_error != null
|
||||
```
|
||||
|
||||
Offen ist **wo** er läuft — beides ist vertretbar:
|
||||
|
||||
- **Prometheus/Alertmanager auf CFGMON** (`threadnet-operating`) — passt zum vorhandenen Alarmweg, braucht aber ein git.lab-Token auf CFGMON und den Tunnel.
|
||||
- **Scheduled CI-Job auf git.lab**, wie `canonize_rotation` im gitops-Repo — läuft im Lab, kein zusätzliches Credential nach außen, meldet sich über eine rote Pipeline. Dafür merkt er nichts, wenn das Lab selbst aus ist (was aber gerade der Fall ist, in dem die Mirrors ohnehin nicht laufen).
|
||||
|
||||
## Abgrenzung
|
||||
|
||||
Nicht Teil dieses Issues: die Rotation der Tokens selbst ([#15](https://git.lab/axion1337.chat/management/-/issues/15)) und die Frage, welches Credential dort hinterlegt ist. Hier geht es allein darum, einen Ausfall **zu bemerken**.
|
||||
|
||||
---
|
||||
*Gefunden beim Session-Abschluss 2026-08-02, beim Nachgehen der Randbedingung aus #15.*
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0029"
|
||||
status: open
|
||||
created: 2026-08-06
|
||||
milestone: M4
|
||||
priority: medium
|
||||
gitlab_iid: "29"
|
||||
related: []
|
||||
---
|
||||
# UI harmonisieren: gleiche Farben und Formen über alle Oberflächen
|
||||
|
||||
> Import aus [management#29](https://git.lab/axion1337.chat/management/-/issues/29) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Die Plattform besteht aus mehreren Oberflächen, die nacheinander im selben Nutzerweg auftauchen — und jede bringt ihr eigenes Design-System mit. Das fällt am stärksten an der Anmeldung auf: Authentik (PatternFly) und der Client (Elements Compound) stehen direkt hintereinander und sehen aus wie zwei verschiedene Produkte.
|
||||
|
||||
## Was zu harmonisieren ist
|
||||
|
||||
| Oberfläche | Design-System | heute eingestellt |
|
||||
|---|---|---|
|
||||
| ThreadNet-Web (Client) | Compound | 17 eigene Themes, Markenfarbe `#ed4f4c` |
|
||||
| Authentik (Anmeldung) | PatternFly | nur `branding_title`/Favicon/Hintergrund; `branding_custom_css` **ungenutzt** |
|
||||
| BookStack | eigenes | sorbs Terrakotta-Beige, liegt nur in der DB |
|
||||
| Docusaurus-Wiki | Infima | bislang nur die Akzentfarbe |
|
||||
| Grafana | eigenes | unangetastet |
|
||||
|
||||
## Woran es konkret hängt
|
||||
|
||||
1. **Farben.** Es gibt bereits eine Markenfarbe (`#ed4f4c`) und sorbs Terrakotta-Palette. Beide sind dokumentiert (`shared/branding.md`), aber nur teilweise ausgerollt.
|
||||
2. **Formen.** Radien, Schatten und Button-Höhen unterscheiden sich zwischen den Systemen — mal rund, mal eckig. Das ist das, was den Bruch spürbar macht, noch vor der Farbe.
|
||||
3. **Typografie.** Bisher nirgends vereinheitlicht.
|
||||
|
||||
## Vorschlag für den Zuschnitt
|
||||
|
||||
Nicht alles auf einmal. Sinnvolle Reihenfolge nach sichtbarer Wirkung pro Aufwand:
|
||||
|
||||
1. **Authentik an den Client angleichen** — der Bruch mitten im Anmeldeweg ist der auffälligste. Hebel ist `branding_custom_css` auf dem Brand-Blueprint, also deklarativ und rückbaubar. ⚠️ Vorher klären, ob Authentiks Flow-Komponenten Shadow DOM nutzen — dann greift normales CSS nicht und es braucht `::part()`-Selektoren.
|
||||
2. **Farbwerte an einer Stelle festschreiben**, statt sie je Oberfläche einzutippen. Heute ist die Kopie in `shared/branding.md` die Quelle; ob daraus etwas Maschinenlesbares wird, ist die eigentliche Entscheidung.
|
||||
3. Wiki und BookStack nachziehen.
|
||||
|
||||
## Vorbedingung
|
||||
|
||||
Die offene Frage aus `shared/branding.md` — ob Terrakotta das Stammschema ablöst oder eine Alternative bleibt — sollte **vorher** entschieden sein. Sonst harmonisiert man auf einen Zielwert, der danach wechselt.
|
||||
|
||||
Aufgenommen aus der Session vom 2026-08-06, in der Titelbild und Authentik-Brand gesetzt wurden.
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0030"
|
||||
status: open
|
||||
created: 2026-08-06
|
||||
milestone: M1
|
||||
priority: medium
|
||||
area: security
|
||||
gitlab_iid: "30"
|
||||
related: []
|
||||
---
|
||||
# Der Restore ist nie geprobt — Sicherungen sind bisher eine Vermutung
|
||||
|
||||
> Import aus [management#30](https://git.lab/axion1337.chat/management/-/issues/30) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Es wird gesichert: jeden Sonntag, alle Dienste, drei Versionen vorgehalten, GitLab auf Overmind mit Datenbank **und** Volumes nach MinIO auf dem DSM. Das ist mehr, als die meisten haben.
|
||||
|
||||
**Was fehlt, ist der Beweis, dass sich daraus etwas wiederherstellen lässt.** Es gibt kein dokumentiertes Verfahren und keinen je durchgespielten Versuch. Eine Suche über `docs/` und `verfahren/` findet nur Erwähnungen in `install.md` — keine Anleitung, keine Protokolle.
|
||||
|
||||
## Warum das der wichtigste der offenen Punkte ist
|
||||
|
||||
Eine Sicherung, die nie zurückgespielt wurde, ist eine **Vermutung**. Die typischen Fehler zeigen sich ausschließlich beim Zurückspielen und nie beim Sichern:
|
||||
|
||||
- die Datenbank ist gesichert, aber ohne das Volume mit den Uploads ist sie wertlos
|
||||
- der Dump ist da, aber der Verschlüsselungsschlüssel lag nur auf dem Host, der weg ist
|
||||
- es liegen drei Versionen, aber alle drei sind seit Wochen leer, weil ein Pfad umgezogen ist und keiner es gemerkt hat
|
||||
- niemand weiß, in welcher Reihenfolge die Dienste hochkommen müssen
|
||||
|
||||
Der letzte Punkt ist hier besonders relevant: **Der SOPS-age-Schlüssel entschlüsselt alle Secrets im Cluster.** Wenn der nur an einer Stelle liegt, ist die Frage nicht, ob die Sicherung funktioniert, sondern ob sie überhaupt etwas nützt.
|
||||
|
||||
## Was zu tun ist
|
||||
|
||||
1. **Zuerst das Billigste:** stichprobenartig in die aktuellen Sicherungen hineinschauen. Sind sie plausibel groß? Enthalten sie, was sie sollen? Das findet stille Ausfälle sofort.
|
||||
2. Ein echtes Wiederherstellungsverfahren schreiben — als Ablauf, nicht als Prosa: welcher Dienst zuerst, woher der SOPS-Schlüssel, woher der kubeconfig.
|
||||
3. **Einmal wirklich durchspielen**, gegen eine Wegwerf-Umgebung, nicht gegen die Produktion. Was dabei fehlt, ist das Ergebnis.
|
||||
4. Ergebnis als Verfahren in `verfahren/` ablegen und danach in bekanntem Abstand wiederholen.
|
||||
|
||||
⚠️ Bewusst **nicht** vorschlagen: die Sicherung erweitern, bevor die vorhandene geprüft ist. Mehr zu sichern, ohne zu wissen, ob das Vorhandene trägt, verschiebt das Problem nur.
|
||||
|
||||
## Grenzen dieses Issues
|
||||
|
||||
Ich kenne den Sicherungsaufbau nur aus deiner Beschreibung und einem Screenshot, nicht aus eigener Anschauung. Der erste Schritt ist deshalb Bestandsaufnahme, nicht Bewertung.
|
||||
|
||||
*Aufgenommen am 2026-08-06 bei einer Bestandsaufnahme der Sicherheitslage.*
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0031"
|
||||
status: open
|
||||
created: 2026-08-09
|
||||
milestone: M1
|
||||
priority: low
|
||||
area: infrastructure
|
||||
gitlab_iid: "31"
|
||||
related: []
|
||||
---
|
||||
# Stillstandsprüfung: GITEA_TOKEN und Authentik-Teil nachziehen
|
||||
|
||||
> Import aus [management#31](https://git.lab/axion1337.chat/management/-/issues/31) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Die [Stillstandsprüfung](../wiki/admin/stillstandspruefung.md) läuft. Offen ist nur noch **ein optionaler Teil**.
|
||||
|
||||
## Was fehlt
|
||||
|
||||
`AUTHENTIK_URL` und `AUTHENTIK_TOKEN` als CI-Variablen im management-Repo. Ohne sie überspringt die Prüfung den Blueprint-Test und weist das im Ergebnis aus:
|
||||
|
||||
```
|
||||
Uebersprungen:
|
||||
- Authentik-Blueprints: AUTHENTIK_URL/AUTHENTIK_TOKEN fehlen —
|
||||
genau der Fall, der uns am laengsten unbemerkt lief
|
||||
```
|
||||
|
||||
## Warum das der ärgerlichste blinde Fleck ist
|
||||
|
||||
Der `matrix-recovery-flow`-Blueprint wurde **tagelang bei jedem Durchlauf verworfen** — während Flux grün meldete, die ConfigMap aktuell war und im Cluster alles gesund aussah. Gefunden wurde es nur, weil jemand für eine ganz andere Sache in die Authentik-Datenbank schaute (gitops#60).
|
||||
|
||||
Von allen sechs stillen Fehlern des Monats ist das der, der am längsten unentdeckt lief. Die Prüfung deckt fünf davon ab — ausgerechnet diesen nicht.
|
||||
|
||||
## Was nötig wäre
|
||||
|
||||
**In Authentik:** *Admin → Verzeichnis → Tokens & App-Passwörter → Erstellen*. Sauber wäre ein eigenes Dienstkonto mit reinem Lesezugriff auf `/api/v3/managed/blueprints/`; ein Token des Admin-Kontos ginge auch, hätte dann aber dessen volle Rechte.
|
||||
|
||||
**In GitLab** (management → Einstellungen → CI/CD → Variablen):
|
||||
|
||||
| Schlüssel | Wert | Flags |
|
||||
|---|---|---|
|
||||
| `AUTHENTIK_URL` | `https://auth.axion1337.chat` | — |
|
||||
| `AUTHENTIK_TOKEN` | das Token | maskiert, geschützt |
|
||||
|
||||
Mehr ist nicht zu tun — der Code steht, er wartet nur auf die Zugänge.
|
||||
|
||||
---
|
||||
|
||||
## Erledigt (2026-08-09)
|
||||
|
||||
- ✅ `GITLAB_TOKEN` hinterlegt, in der CI verifiziert
|
||||
- ✅ Zeitplan `Stillstandsprüfung (täglich)` angelegt, 6:17 Europe/Berlin
|
||||
- ✅ Erster Lauf über den Zeitplan durchgeführt: fand in der CI **dieselben 7 Befunde** wie lokal — kein Unterschied zwischen den Umgebungen
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0032"
|
||||
status: open
|
||||
created: 2026-08-09
|
||||
milestone: M2
|
||||
priority: medium
|
||||
area: infrastructure
|
||||
gitlab_iid: "32"
|
||||
related: []
|
||||
---
|
||||
# gameserver hat keinen Push-Mirror — und auf Gitea liegt ein anderer Stand
|
||||
|
||||
> Import aus [management#32](https://git.lab/axion1337.chat/management/-/issues/32) (2026-08-11). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei (ADR-0012).
|
||||
|
||||
Gefunden beim ersten Lauf der Stillstandsprüfung (2026-08-09) — **beides war vorher niemandem bekannt.**
|
||||
|
||||
Die Gruppe `axion1337.chat` hat **acht** Projekte, nicht sechs. Zwei davon haben **keinen aktiven Push-Mirror**:
|
||||
|
||||
| Projekt | Zustand |
|
||||
|---|---|
|
||||
| `game-operating` | in einer Session am 2026-08-06 angelegt, nie gespiegelt — auf Gitea existiert es **gar nicht** (HTTP 404) |
|
||||
| `gameserver` | kein Mirror konfiguriert; auf Gitea liegt ein gleichnamiges Repo mit **anderem** Stand (`d5c6ccb2` vs. `48441a50`) |
|
||||
|
||||
## Warum das zählt
|
||||
|
||||
`CLAUDE.md` sagt: *„Gespiegelt wird nur die Gruppe `axion1337.chat`"* — als Eigenschaft der Gruppe, nicht als Liste einzelner Repos. Diese beiden widersprechen dem still. Wer sich auf die Aussage verlässt, nimmt an, dass ein Verlust von git.lab folgenlos wäre. Für diese beiden stimmt das nicht.
|
||||
|
||||
⚠️ Bei `gameserver` ist es unangenehmer als bei `game-operating`: Dort existieren **zwei Repos mit demselben Namen und verschiedenen Ständen**. Wer das eine für eine Kopie des anderen hält, liegt falsch.
|
||||
|
||||
## Zu entscheiden
|
||||
|
||||
Pro Repo eines von beidem:
|
||||
|
||||
1. **Push-Mirror nachziehen** — dann stimmt die Topologie wieder. Bei `gameserver` ⚠️ **vorher prüfen, welcher Stand der richtige ist**: Ein Mirror überschreibt die Gitea-Seite per Force, und der dortige Stand ginge verloren.
|
||||
2. **Ausnahme begründen** — dann gehört sie in die `CLAUDE.md`, nicht ins Schweigen. Für `game-operating` ist das plausibel: Das Repo bildet nur ein Compose-Setup ab, es ist ausdrücklich „Abbild, keine Quelle".
|
||||
|
||||
Die Prüfung meldet beide so lange, bis eines von beidem passiert ist — das ist beabsichtigt.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0033"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: low
|
||||
host: overmind
|
||||
related: []
|
||||
---
|
||||
|
||||
# OVERMIND-01 — element-desktop-build von rohana in die Lab-Registry umziehen
|
||||
|
||||
> Angelegt bei der neckbeard-Migration (Feldtest-Befund F-004: dieser
|
||||
> Arbeitspunkt lebte nur in Host-Prosa und war für Board, Meilenstein
|
||||
> und Priorität unsichtbar). `gitlab_iid` folgt mit dem ersten
|
||||
> Spiegel-Lauf.
|
||||
|
||||
ThreadNet-Web-CI umstellen: `desktop_image`-Push-Ziel und
|
||||
`desktop_linux`-Image-Referenz von rohana auf `registry.git.lab`.
|
||||
Bewusst zurückgestellt, bis kein Auto-Job das alte Image parallel
|
||||
referenziert — Reihenfolge: erst neues Image bauen, dann Referenz
|
||||
umstellen. Kontext: [overmind](../wiki/admin/overmind.md).
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0034"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
host: cfgmon
|
||||
related: []
|
||||
---
|
||||
|
||||
# CFGMON-11 — Gitea-CI-Rückbau abschließen (sicher rückbaubare Schritte)
|
||||
|
||||
> Angelegt bei der neckbeard-Migration (Feldtest-Befund F-004).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
Die als „sicher rückbaubar" dokumentierten Schritte ausführen
|
||||
([cfgmon](../wiki/admin/cfgmon.md), Abschnitt Gitea-CI-Rückbau):
|
||||
Actions-Toggle bei ThreadNet-Web/threadnet-call deaktivieren, die
|
||||
ersetzten Workflow-Dateien entfernen, Runner-Identität deregistrieren —
|
||||
und den **npm-Token aus der untracked `.npmrc` revoken/rotieren**
|
||||
(Klartext-Fund vom 2026-07-30; der Anteil ist der Grund für
|
||||
`priority: medium`). Dazu der kosmetische Handgriff auf CFGMON:
|
||||
`cd /opt/thread-net-git && git checkout main && git pull`.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0035"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Rollout Gruppenregeln-Pointer: `axion1337.chat-gitops`
|
||||
|
||||
> Folge-Issue aus [ADR-0013](../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)
|
||||
> (Feldtest F-011: 4 von 5 Komponenten trugen keine Pointer-Datei).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
In `axion1337.chat-gitops` anlegen: `CLAUDE.md` als Ein-Zeilen-Pointer und ein
|
||||
`AGENTS.md` mit **nur** Projektspezifika plus Verweis auf die
|
||||
Gruppenregeln (management-Repo, git.lab + rohana-Mirror-URL). Bestehende
|
||||
projektspezifische CLAUDE.md-Inhalte (gitops) bleiben erhalten und
|
||||
rücken unter den Pointer. Danach meldet `gruppenpruefung.py` die
|
||||
Komponente grün.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0036"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Rollout Gruppenregeln-Pointer: `ThreadNet-Web`
|
||||
|
||||
> Folge-Issue aus [ADR-0013](../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)
|
||||
> (Feldtest F-011: 4 von 5 Komponenten trugen keine Pointer-Datei).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
In `ThreadNet-Web` anlegen: `CLAUDE.md` als Ein-Zeilen-Pointer und ein
|
||||
`AGENTS.md` mit **nur** Projektspezifika plus Verweis auf die
|
||||
Gruppenregeln (management-Repo, git.lab + rohana-Mirror-URL). Bestehende
|
||||
projektspezifische CLAUDE.md-Inhalte (gitops) bleiben erhalten und
|
||||
rücken unter den Pointer. Danach meldet `gruppenpruefung.py` die
|
||||
Komponente grün.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0037"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Rollout Gruppenregeln-Pointer: `threadnet-call`
|
||||
|
||||
> Folge-Issue aus [ADR-0013](../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)
|
||||
> (Feldtest F-011: 4 von 5 Komponenten trugen keine Pointer-Datei).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
In `threadnet-call` anlegen: `CLAUDE.md` als Ein-Zeilen-Pointer und ein
|
||||
`AGENTS.md` mit **nur** Projektspezifika plus Verweis auf die
|
||||
Gruppenregeln (management-Repo, git.lab + rohana-Mirror-URL). Bestehende
|
||||
projektspezifische CLAUDE.md-Inhalte (gitops) bleiben erhalten und
|
||||
rücken unter den Pointer. Danach meldet `gruppenpruefung.py` die
|
||||
Komponente grün.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0038"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Rollout Gruppenregeln-Pointer: `thread-net-git`
|
||||
|
||||
> Folge-Issue aus [ADR-0013](../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)
|
||||
> (Feldtest F-011: 4 von 5 Komponenten trugen keine Pointer-Datei).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
In `thread-net-git` anlegen: `CLAUDE.md` als Ein-Zeilen-Pointer und ein
|
||||
`AGENTS.md` mit **nur** Projektspezifika plus Verweis auf die
|
||||
Gruppenregeln (management-Repo, git.lab + rohana-Mirror-URL). Bestehende
|
||||
projektspezifische CLAUDE.md-Inhalte (gitops) bleiben erhalten und
|
||||
rücken unter den Pointer. Danach meldet `gruppenpruefung.py` die
|
||||
Komponente grün.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0039"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: medium
|
||||
related:
|
||||
- "docs/adr/0013-gruppenregeln-kanonisch-mit-pruefung.md"
|
||||
---
|
||||
|
||||
# Rollout Gruppenregeln-Pointer: `threadnet-operating`
|
||||
|
||||
> Folge-Issue aus [ADR-0013](../adr/0013-gruppenregeln-kanonisch-mit-pruefung.md)
|
||||
> (Feldtest F-011: 4 von 5 Komponenten trugen keine Pointer-Datei).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
In `threadnet-operating` anlegen: `CLAUDE.md` als Ein-Zeilen-Pointer und ein
|
||||
`AGENTS.md` mit **nur** Projektspezifika plus Verweis auf die
|
||||
Gruppenregeln (management-Repo, git.lab + rohana-Mirror-URL). Bestehende
|
||||
projektspezifische CLAUDE.md-Inhalte (gitops) bleiben erhalten und
|
||||
rücken unter den Pointer. Danach meldet `gruppenpruefung.py` die
|
||||
Komponente grün.
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0040"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: low
|
||||
related:
|
||||
- "docs/design/done/2026-08-11-neckbeard-migration.md"
|
||||
---
|
||||
|
||||
# neckbeard-Rückmeldungen aus dem Feldtest einreichen
|
||||
|
||||
> Eigener Akt, bewusst nicht Teil der Migration (Nicht-Ziel im Design).
|
||||
> `gitlab_iid` folgt mit dem ersten Spiegel-Lauf.
|
||||
|
||||
Das fertige Übergabedokument liegt unter
|
||||
[docs/sources/migration/neckbeard-uebergabe-feldtest.md](../sources/migration/neckbeard-uebergabe-feldtest.md)
|
||||
— von dort als Issues im neckbeard-Repo (`oss-projekte/ai/neckbeard`) einreichen,
|
||||
mit Feldtest-Evidenz aus Gate 2 des Design-Dokuments:
|
||||
|
||||
1. Viele Repos, ein Regelwerk (Lücke 1; hiesige Lösung: ADR-0013)
|
||||
2. Komponenten-Artefakt (Lücke 2)
|
||||
3. Meilenstein-Konzept (Lücke 3)
|
||||
4. SHA-Auflösung in validate (Lücke 4; Referenz: `pruefe_prosa.py`)
|
||||
5. Git-Hygiene-Prüffamilie (Lücke 5; Referenz: `gruppenpruefung.py`)
|
||||
6. Sperrlisten für stillgelegte externe Ziele (Lücke 6, Teil-Lösung)
|
||||
7. Prioritätsfeld: Feldtest-Evidenz für das im Schöpfungs-AAR vertagte
|
||||
Revisit (71/71 mit Priorität, getrennt vom Meilenstein)
|
||||
8. `validate.py` lehnt Verzeichnis-Links ab (Meinungsfrage)
|
||||
9. Definierter Ort für Projektregeln im übernommenen AGENTS.md
|
||||
10. ADR-Pflicht bei dauerhaften Ausnahmen fehlt upstream
|
||||
11. Stillstandsprüfungs-Prinzipien als Muster für eine
|
||||
Laufzeit-Prüf-Familie neben validate.py
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0041"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: low
|
||||
related: []
|
||||
---
|
||||
|
||||
# wartegrund der 7 importierten waiting-Issues präzisieren
|
||||
|
||||
> Nacharbeit aus dem Issue-Import (Protokoll unter
|
||||
> `docs/sources/migration/`). `gitlab_iid` folgt mit dem Spiegel-Lauf.
|
||||
|
||||
0002, 0004, 0008, 0014, 0021, 0025, 0027 tragen den generischen
|
||||
Import-`wartegrund` „Grund im GitLab-Verlauf". Im nächsten Refinement
|
||||
je Issue den echten Grund eintragen (alte Regel: `wartet` nur mit
|
||||
benanntem Grund).
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0042"
|
||||
status: open
|
||||
created: 2026-08-11
|
||||
milestone: M2
|
||||
priority: high
|
||||
related:
|
||||
- "docs/design/done/2026-08-11-neckbeard-migration.md"
|
||||
---
|
||||
|
||||
# Migration in Betrieb nehmen: Push, erster Spiegel-Lauf, CI-Schedule
|
||||
|
||||
> Die Schritte, die nur sorb ausführt (Nicht-Ziele der Migration:
|
||||
> kein Push, kein API-Write durch die Session).
|
||||
|
||||
1. ~~Branch `Neckbeard-v0.1.1-migration-1` sichten und nach `main`
|
||||
bringen; Push über git.lab (Mirror zieht nach).~~ ✅ Erledigt
|
||||
2026-08-11 (Fast-Forward-Merge + Push, von sorb beauftragt).
|
||||
2. Ersten Spiegel-Lauf ausführen: `python3 scripts/spiegel_issues.py
|
||||
--ausfuehren` (legt 0033–0042 auf GitLab an); danach die vergebenen
|
||||
iids als `gitlab_iid` nachtragen — ab dann meldet
|
||||
`gruppenpruefung.py` die Hinweise nicht mehr.
|
||||
3. CI-Schedule für den Job `gruppenpruefung` anlegen (wie
|
||||
Stillstandsprüfung; Gruppen-Token mit `read_api` liegt als maskierte
|
||||
Variable bereits vor, siehe management#31).
|
||||
4. gitops#61 einen Meilenstein geben (M1 oder M5 an der
|
||||
ADR-0010-Trennlinie) — der rote Befund der Gruppenprüfung ist die
|
||||
Erinnerung.
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0000"
|
||||
status: open # open | in-progress | done | rejected
|
||||
created: YYYY-MM-DD
|
||||
related: [] # design docs, ADRs, other issues
|
||||
---
|
||||
|
||||
<!-- Copy to docs/issues/NNNN-slug.md. Delete comments when filling in. -->
|
||||
|
||||
# Issue-0000: Title
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
<!-- What's wrong or missing, and why it matters. One paragraph. -->
|
||||
|
||||
## Acceptance
|
||||
|
||||
<!-- When is this issue done? Verifiable, like every other criterion
|
||||
in this framework. -->
|
||||
|
||||
## Notes
|
||||
|
||||
<!-- Optional: context, links, findings gathered along the way.
|
||||
Rules: status is the single source of truth and lives here in the
|
||||
frontmatter — STATUS.md is generated, never edited. An issue that
|
||||
starts real work links its design doc in `related`. Closed means
|
||||
status: done (or rejected, with a one-line reason in Notes) —
|
||||
the file stays; git is the history. -->
|
||||
@@ -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.
|
||||
@@ -0,0 +1,93 @@
|
||||
# Handoff to the neckbeard repo — field test results, v0.1.1
|
||||
|
||||
Written 2026-08-11 at the close of the first real neckbeard adoption.
|
||||
In English because it is destined for the neckbeard repo, whose
|
||||
artifacts are English by its own convention. This file is the frozen
|
||||
handoff record (management repo, `docs/sources/migration/`); carrying
|
||||
its content into neckbeard issues is tracked as management issue 0040.
|
||||
|
||||
## What happened
|
||||
|
||||
- **Field test** against neckbeard `v0.1.1`
|
||||
(`823a08cac6b03a47d7e2f661200a49ac6e09d38d`): two sessions on the
|
||||
`axion1337.chat/management` repo. Session 1 (branch
|
||||
`Neckbeard-v0.1.1-analyse-1`) produced 17 evidence-backed findings —
|
||||
read `analysis/REPORT.md` there, especially the
|
||||
pattern → mechanism → implication table. Session 2 migrated the repo
|
||||
to neckbeard through **all gates of a size-L undertaking**: Gate 0
|
||||
(PROJECT.md), design doc with Gates 1–5, five vertical slices, each
|
||||
with verification evidence and a human STOP.
|
||||
- Result: `docs/design/done/2026-08-11-neckbeard-migration.md` on
|
||||
`main` of the management repo — including the Gate-5 AAR and the
|
||||
two-way harvest (old approach's value folded into neckbeard before
|
||||
adoption).
|
||||
|
||||
## Relevant for versioning (ADR-0006)
|
||||
|
||||
ADR-0006 names "the first completed size-L run in a real project" as
|
||||
the sensible trigger for considering `v1.0.0`. **That run now exists
|
||||
and is documented.** The schema and rule set survived it, with the
|
||||
extensions below — worth weighing before any 1.0 decision.
|
||||
|
||||
## Feedback items, each with field evidence
|
||||
|
||||
Reference implementations live in the management repo (`scripts/`,
|
||||
`schema.yaml`, `docs/components/`); findings F-NNN in the analysis
|
||||
branch.
|
||||
|
||||
1. **Many repos, one ruleset.** ADR-0001 ends at the repo boundary; a
|
||||
five-component group has no defined sharing mechanism. Solved
|
||||
project-side as pointer + deterministic presence check
|
||||
(management ADR-0013). Evidence: F-011 — 4 of 5 components carried
|
||||
no instruction file and nothing noticed.
|
||||
2. **Components artifact.** No artifact type declares "these are the
|
||||
repos and their canonical names"; slug drift was unrepresentable
|
||||
(F-008). Project-side: `component` type, filename = canonical slug.
|
||||
3. **Milestone concept.** No field groups issues by what they pay
|
||||
into; the project uses milestones on 100% of open issues (F-014).
|
||||
Project-side: required `milestone` enum on issues.
|
||||
4. **SHA citations in prose are never resolved.** F-012: six orphaned
|
||||
citations, mechanically uncheckable. Reference: `pruefe_prosa.py`
|
||||
(resolution via repo, rewrite-mapping table, optional clones, plus
|
||||
a curated exemption list — hex words are not always git SHAs:
|
||||
Authentik uids and Alertmanager silence IDs both matched).
|
||||
5. **Git-level hygiene is outside the framework's view** while
|
||||
carrying the project's most sensitive claims (F-002/F-003: 222
|
||||
real-clock commits by own identities believed anonymised).
|
||||
Reference: `gruppenpruefung.py` hygiene check.
|
||||
6. **External link targets are never checked.** A live doc routed to a
|
||||
retired tracker (F-005 — eight dead links found in practice).
|
||||
Deterministic partial solution: a denylist of retired URL patterns;
|
||||
full reachability checking deliberately rejected (network-bound).
|
||||
7. **Priority field.** The creation AAR filed it as YAGNI with
|
||||
"revisit via refinement". Field evidence for the revisit: 71/71
|
||||
open issues carry exactly one priority, cleanly distinct from the
|
||||
milestone ("how urgent" vs "what it pays into").
|
||||
8. **`validate.py` rejects directory links** (`[x](dir/)`), which
|
||||
GitLab renders fine. Opinion question; cost us three pre-existing
|
||||
"broken" links.
|
||||
9. **Adopted AGENTS.md has no defined place for project rules.**
|
||||
Solved as: upstream sections byte-true, then a marked project
|
||||
section; a byte-compare check against a vendored pristine baseline
|
||||
(`docs/sources/upstream/`) turns silent framework-file rewrites
|
||||
into red CI. The baseline answers a real adopter question ("will
|
||||
agents rewrite AGENTS.md?") — consider making it part of the
|
||||
adoption path.
|
||||
10. **ADR duty for permanent exceptions** exists in this project's old
|
||||
ruleset and proved itself (documented-but-undecided exceptions are
|
||||
a named failure mode); upstream has no such rule.
|
||||
11. **A runtime check family beside validate.py.** The project's
|
||||
Stillstandsprüfung principles held up well and generalize: checks
|
||||
only from real incidents, "cannot check" is a finding not a skip,
|
||||
abort instead of silently skipping, project lists read at runtime
|
||||
never maintained in code.
|
||||
|
||||
## Where to look
|
||||
|
||||
| What | Where |
|
||||
|---|---|
|
||||
| Field-test findings + data | management branch `Neckbeard-v0.1.1-analyse-1`, `analysis/` |
|
||||
| Migration design + AAR | `docs/design/done/2026-08-11-neckbeard-migration.md` (main) |
|
||||
| Schema extensions | `schema.yaml` (flagged header) vs `docs/sources/upstream/neckbeard-v0.1.1/schema.yaml` |
|
||||
| New check scripts | `scripts/pruefe_upstream_drift.py`, `pruefe_prosa.py`, `gruppenpruefung.py`, `spiegel_issues.py` |
|
||||
| Harvested pitfalls | `docs/wiki/stolpersteine/neckbeard-migration.md` |
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
name: karpathy-guidelines
|
||||
description: Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# Karpathy Guidelines
|
||||
|
||||
Behavioral guidelines to reduce common LLM coding mistakes, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls.
|
||||
|
||||
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
||||
|
||||
## 1. Think Before Coding
|
||||
|
||||
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
||||
|
||||
Before implementing:
|
||||
- State your assumptions explicitly. If uncertain, ask.
|
||||
- If multiple interpretations exist, present them - don't pick silently.
|
||||
- If a simpler approach exists, say so. Push back when warranted.
|
||||
- If something is unclear, stop. Name what's confusing. Ask.
|
||||
|
||||
## 2. Simplicity First
|
||||
|
||||
**Minimum code that solves the problem. Nothing speculative.**
|
||||
|
||||
- No features beyond what was asked.
|
||||
- No abstractions for single-use code.
|
||||
- No "flexibility" or "configurability" that wasn't requested.
|
||||
- No error handling for impossible scenarios.
|
||||
- If you write 200 lines and it could be 50, rewrite it.
|
||||
|
||||
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
||||
|
||||
## 3. Surgical Changes
|
||||
|
||||
**Touch only what you must. Clean up only your own mess.**
|
||||
|
||||
When editing existing code:
|
||||
- Don't "improve" adjacent code, comments, or formatting.
|
||||
- Don't refactor things that aren't broken.
|
||||
- Match existing style, even if you'd do it differently.
|
||||
- If you notice unrelated dead code, mention it - don't delete it.
|
||||
|
||||
When your changes create orphans:
|
||||
- Remove imports/variables/functions that YOUR changes made unused.
|
||||
- Don't remove pre-existing dead code unless asked.
|
||||
|
||||
The test: Every changed line should trace directly to the user's request.
|
||||
|
||||
## 4. Goal-Driven Execution
|
||||
|
||||
**Define success criteria. Loop until verified.**
|
||||
|
||||
Transform tasks into verifiable goals:
|
||||
- "Add validation" → "Write tests for invalid inputs, then make them pass"
|
||||
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
||||
- "Refactor X" → "Ensure tests pass before and after"
|
||||
|
||||
For multi-step tasks, state a brief plan:
|
||||
```
|
||||
1. [Step] → verify: [check]
|
||||
2. [Step] → verify: [check]
|
||||
3. [Step] → verify: [check]
|
||||
```
|
||||
|
||||
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# AGENTS.md — Canonical Agent Instructions
|
||||
|
||||
Canonical instruction set for any coding agent working in this repository
|
||||
(Claude Code, GPT-OSS harnesses, others). `CLAUDE.md` points here.
|
||||
This file is loaded into every session — keep it short. Process details
|
||||
live in `WORKFLOW.md`; read that when a task begins, not preemptively.
|
||||
|
||||
Tradeoff: these rules bias toward caution over speed. For trivial tasks,
|
||||
use judgment — but say so.
|
||||
|
||||
## 1. Operating Rules
|
||||
|
||||
### Think before coding
|
||||
- State your assumptions explicitly. If uncertain, ask.
|
||||
- If multiple interpretations exist, present them — don't pick silently.
|
||||
- If a simpler approach exists, say so. Push back when warranted.
|
||||
- If something is unclear, stop. Name what's confusing. Ask.
|
||||
|
||||
### Simplicity first
|
||||
- Minimum code that solves the problem. Nothing speculative.
|
||||
- No features beyond what was asked. No abstractions for single-use code.
|
||||
- No "flexibility" or "configurability" that wasn't requested.
|
||||
- No error handling for impossible scenarios.
|
||||
- If you write 200 lines and it could be 50, rewrite it.
|
||||
- Test: "Would a senior engineer call this overcomplicated?" If yes, simplify.
|
||||
- Before writing new code, stop at the first rung that holds:
|
||||
needed at all? → codebase already has it? → stdlib? → platform-native?
|
||||
→ installed dependency? → one line? → only then: the minimum that works.
|
||||
(Ladder after ponytail, MIT.)
|
||||
- Never cut, at any rung: trust-boundary validation, data-loss handling,
|
||||
security, accessibility.
|
||||
- Lazy about the solution, never about reading the code first.
|
||||
|
||||
### Surgical changes
|
||||
- Touch only what you must. Match existing style, even if you'd differ.
|
||||
- Don't "improve" adjacent code, comments, or formatting.
|
||||
- Don't refactor things that aren't broken.
|
||||
- If you notice unrelated dead code, mention it — don't delete it.
|
||||
- Remove imports/variables/functions that YOUR changes made unused;
|
||||
leave pre-existing dead code alone unless asked.
|
||||
- Every changed line must trace directly to the request.
|
||||
|
||||
### Goal-driven execution
|
||||
- Transform tasks into verifiable goals:
|
||||
"fix the bug" → "write a test that reproduces it, then make it pass".
|
||||
- For multi-step work, state a brief plan: step → verify, step → verify.
|
||||
- A task is well-defined only if it names all four:
|
||||
**files, action, verify, done.** Missing one? The task is too vague — say so.
|
||||
|
||||
### Verification before completion
|
||||
- Never claim something works without evidence: a test run, command
|
||||
output, a rendered result. "Should work" is not a status.
|
||||
- Report every task/slice with exactly one status:
|
||||
`DONE` | `DONE_WITH_CONCERNS` | `NEEDS_CONTEXT` | `BLOCKED`.
|
||||
- Uncertainty is reported, never swallowed. Flag your shakiest calls.
|
||||
|
||||
## 2. Project Initialization (Gate 0)
|
||||
|
||||
At session start, read `PROJECT.md`. If it does not exist, initialization
|
||||
is your first task: before anything else, ask the Gate 0 questions defined
|
||||
in `WORKFLOW.md` — response language, size-S gate exception (yes/no),
|
||||
one-line project purpose, audience — write the answers to `PROJECT.md`,
|
||||
and have `validate.py` accept it. Never guess these answers; ask.
|
||||
|
||||
## 3. Workflow
|
||||
|
||||
For anything beyond a trivial change, read `WORKFLOW.md` and follow its
|
||||
gates. At task start, propose a size class (S/M/L); the human confirms
|
||||
(possibly batched later). **Never advance past a gate without explicit
|
||||
human approval** — sole exception: size-S tasks, and only if `PROJECT.md`
|
||||
explicitly grants that exception.
|
||||
|
||||
## 4. Repository Map
|
||||
|
||||
| Path | Purpose |
|
||||
|---|---|
|
||||
| `WORKFLOW.md` | Gate 0 (init) + Gates 1–5, size classes, debugging path, session handoff, refinement ritual |
|
||||
| `PROJECT.md` | Per-project answers from Gate 0: language, size-S exception, purpose, audience |
|
||||
| `STATUS.md` | Generated overview: open issues, active designs, recent ADRs — do not edit by hand |
|
||||
| `schema.yaml` | Frontmatter schema — single source of truth for artifact structure |
|
||||
| `docs/adr/` | Architecture Decision Records — binding; never edited, only superseded |
|
||||
| `docs/design/` | One design doc per undertaking; completed ones move to `done/` |
|
||||
| `docs/aar/` | Standalone After Action Reviews (incidents, major deviations only) |
|
||||
| `docs/issues/` | In-repo issues, one file each; status lives in frontmatter |
|
||||
| `docs/wiki/` | Wiki areas as folders, created on demand — rules in `docs/wiki/index.md` |
|
||||
| `docs/sources/` | Immutable original sources; wiki pages cite them — read-only for agents |
|
||||
| `scripts/` | Deterministic tooling: `validate.py`, `gen_status.py` |
|
||||
|
||||
Before proposing options (Gate 2), read the relevant ADRs and AARs first —
|
||||
past decisions and learnings are input, not trivia.
|
||||
|
||||
## 5. Artifact Rules
|
||||
|
||||
- All artifacts are standard Markdown with YAML frontmatter conforming to
|
||||
`schema.yaml`. Standard links only (`[text](path.md)`), no wikilinks.
|
||||
Diagrams as Mermaid. This keeps every artifact portable across LLMs,
|
||||
GitLab, and Obsidian.
|
||||
- Never invent frontmatter fields or status values. `validate.py` is
|
||||
authoritative; if it rejects your artifact, fix the artifact, not the
|
||||
validator.
|
||||
- Deterministic jobs (status generation, validation, link checks) are done
|
||||
by scripts, not by you. If a deterministic job lacks a script, propose
|
||||
one instead of doing it by inference.
|
||||
@@ -0,0 +1 @@
|
||||
Read AGENTS.md — the canonical instruction file for this repository. All rules live there.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Herkunft dieser Baseline
|
||||
|
||||
Unveränderte Originale aus **neckbeard v0.1.1**, Commit
|
||||
`823a08cac6b03a47d7e2f661200a49ac6e09d38d` (`main`, sauber), Origin
|
||||
`https://git.lab/oss-projekte/ai/neckbeard.git` — der Stand, gegen den
|
||||
der Feldtest (Branch `Neckbeard-v0.1.1-analyse-1`) gemessen hat und aus
|
||||
dem die Migration übernommen wurde (Design:
|
||||
`docs/design/done/2026-08-11-neckbeard-migration.md`).
|
||||
|
||||
Zweck: Byte-Baseline für `scripts/pruefe_upstream_drift.py`. Ein
|
||||
Framework-Upgrade ersetzt diese Dateien bewusst und in einem eigenen
|
||||
Commit — nie beiläufig.
|
||||
|
||||
| Datei hier | Arbeitskopie | Prüfung |
|
||||
|---|---|---|
|
||||
| `AGENTS.md` | `/AGENTS.md` | Präfix bis zur Marke `<!-- projektabschnitt -->` |
|
||||
| `CLAUDE.md` | `/CLAUDE.md` | byte-identisch |
|
||||
| `WORKFLOW.md` | `/WORKFLOW.md` | byte-identisch |
|
||||
| `templates/adr-template.md` | `docs/adr/template.md` | byte-identisch |
|
||||
| `templates/design-template.md` | `docs/design/template.md` | byte-identisch |
|
||||
| `templates/aar-template.md` | `docs/aar/template.md` | byte-identisch |
|
||||
| `templates/issue-template.md` | `docs/issues/template.md` | byte-identisch |
|
||||
| `schema.yaml` | `/schema.yaml` | **erklärt projekterweitert** — nur Diff-Referenz |
|
||||
| `scripts/validate.py` | `scripts/validate.py` | **erklärt projekterweitert** — nur Diff-Referenz |
|
||||
| `scripts/gen_status.py` | `scripts/gen_status.py` | **erklärt projekterweitert** — nur Diff-Referenz |
|
||||
@@ -0,0 +1,140 @@
|
||||
# WORKFLOW.md — Gates, Sizing, and Rituals
|
||||
|
||||
Read this when a task begins, not preemptively. `AGENTS.md` holds the
|
||||
always-on rules; this file holds the process.
|
||||
|
||||
## Size Classes
|
||||
|
||||
Propose one at task start; the human confirms — individually, or batched
|
||||
at the next refinement session.
|
||||
|
||||
| Class | Scope | Process |
|
||||
|---|---|---|
|
||||
| S | One file / one small change, no design decisions | Direct. AGENTS.md rules only. The one-line go-ahead **before starting is the stop** — waived only if `PROJECT.md` grants the size-S exception. |
|
||||
| M | Few files, minor decisions, fits one session | Slice plan in chat, no file. **STOP: plan approval before any code.** Then implement; each slice reports evidence and status inline. Gate 5 is a short AAR note in chat, filed to the wiki only if it produced a real learning. |
|
||||
| L | New feature, multiple files or sessions, real decisions | Full design doc in `docs/design/` following Gates 1–5 below. |
|
||||
|
||||
When in doubt between two classes, pick the larger.
|
||||
|
||||
## Gate 0 — Project Initialization
|
||||
|
||||
Runs once per project, triggered by a missing `PROJECT.md`. Ask, never guess:
|
||||
|
||||
1. Response language? (e.g. de / en)
|
||||
2. Size-S gate exception granted? (yes / no)
|
||||
3. One-line project purpose?
|
||||
4. Audience — who uses this besides the owner? (Drives which wiki areas
|
||||
become mandatory later; see `docs/wiki/index.md`.)
|
||||
|
||||
Write the answers to `PROJECT.md` (frontmatter per `schema.yaml`), run
|
||||
`validate.py`, and confirm the result with the human.
|
||||
|
||||
## Gates 1–5 (size L)
|
||||
|
||||
Each gate is a section of the design doc. A gate ends with **STOP**:
|
||||
present the section, wait for explicit approval. Do not pre-fill later
|
||||
sections.
|
||||
|
||||
### Gate 1 — Product
|
||||
- Problem statement: what user problem, for whom.
|
||||
- Verifiable acceptance criterion. A real number where one exists;
|
||||
otherwise a concretely checkable outcome. "Works" is not a criterion.
|
||||
- Non-goals: what this deliberately does not do.
|
||||
- Announcement paragraph (3–5 sentences): what it is, who it's for, why
|
||||
it's good. If you can't write it, the product isn't understood yet.
|
||||
- UI involved? Plain-HTML mockups of the affected screens.
|
||||
|
||||
**STOP.**
|
||||
|
||||
### Gate 2 — Architecture
|
||||
- Read first: the actual codebase, relevant ADRs, relevant AARs.
|
||||
Past decisions and learnings are input, not trivia.
|
||||
- How it fits the real system: endpoints, tables/schemas, query
|
||||
outlines, the end-to-end flow (Mermaid).
|
||||
- Constraints: non-functional requirements, proportional to the project.
|
||||
- Options & trade-offs where more than one viable way exists: pro/contra
|
||||
each, chosen option, and why. Feature-local decisions stay here.
|
||||
- Lasting directional decisions discovered here become ADRs (one each),
|
||||
linked from the design doc.
|
||||
|
||||
**STOP.**
|
||||
|
||||
### Gate 3 — Program Design
|
||||
- File locations: exact paths, new and touched.
|
||||
- Types and method signatures — no bodies.
|
||||
- Call stack for the main flow(s).
|
||||
- What the tests will assert.
|
||||
- Boundaries: an explicit DO NOT CHANGE list.
|
||||
- Shakiest calls: name the decisions you are least confident about.
|
||||
|
||||
**STOP.**
|
||||
|
||||
### Gate 4 — Vertical Slices
|
||||
- Slice 1 is the tracer bullet: a thin end-to-end path that runs
|
||||
(mocks and stubs allowed). Only then real logic, one testable slice
|
||||
at a time. Never build layer-by-layer horizontally.
|
||||
- Every slice lists its tasks; every task names **files, action,
|
||||
verify, done**.
|
||||
- Each slice ends with verification evidence, a status
|
||||
(`DONE` | `DONE_WITH_CONCERNS` | `NEEDS_CONTEXT` | `BLOCKED`),
|
||||
and a **STOP** for human review before the next slice.
|
||||
|
||||
### Gate 5 — Closeout
|
||||
- AAR section in the design doc: planned / actual / why the
|
||||
difference / learnings.
|
||||
- Harvest: learnings useful to future readers go to the wiki
|
||||
(FAQ, Stolpersteine) with source links. A missing or wrong framework
|
||||
rule becomes a framework issue or update.
|
||||
- Good analyses produced along the way may be filed as wiki pages
|
||||
(with citations) instead of dying in chat history.
|
||||
- Move the design doc to `docs/design/done/`. Run `gen_status.py`.
|
||||
|
||||
## Debugging Path
|
||||
|
||||
For bugs and incidents, any size:
|
||||
|
||||
1. Reproduce first. No reproduction, no fix.
|
||||
2. Hypothesize the root cause; verify the hypothesis with evidence
|
||||
before changing anything.
|
||||
3. Route the failure before fixing (diagnostic failure routing):
|
||||
- **Intent issue** — we built toward the wrong goal → back to Gate 1.
|
||||
- **Spec issue** — the design/plan was wrong → fix the spec
|
||||
(Gate 2/3), then the code.
|
||||
- **Code issue** — plan right, code wrong → fix in place.
|
||||
4. Fix, plus a test that would have caught it.
|
||||
5. Incidents and major misdiagnoses get a standalone AAR in `docs/aar/`.
|
||||
|
||||
## Session Handoff
|
||||
|
||||
- When a slice completes, or context quality degrades, write the current
|
||||
state into the design doc's **Handoff block** — done slices, open
|
||||
decisions, next step — then start a fresh session that resumes from
|
||||
the doc. The doc is the memory; the session is disposable.
|
||||
- End every working session by answering: "Which choices did I make that
|
||||
I'm least confident about?" File the answer in the design doc.
|
||||
|
||||
## Refinement Session
|
||||
|
||||
A recurring, human-triggered ritual. Agenda:
|
||||
|
||||
1. Batched confirmations: size classes and small approvals queued since
|
||||
last time.
|
||||
2. Backlog triage over `docs/issues/`: close, reprioritize, split.
|
||||
3. AAR harvest: walk recent AARs; update the wiki (FAQ, Stolpersteine);
|
||||
propose framework changes.
|
||||
4. Wiki lint (content-level, beyond `validate.py`): contradictions
|
||||
between pages, claims superseded by newer sources, orphan pages,
|
||||
missing cross-references, gaps worth a new page or a web search.
|
||||
5. STATUS review: anything stale or surprising in `STATUS.md`.
|
||||
|
||||
## Knowledge Handling (summary)
|
||||
|
||||
Full rules live in `docs/wiki/index.md`. The short version:
|
||||
|
||||
- Original sources live in `docs/sources/`, immutable — agents read
|
||||
them, never modify them. Wiki pages cite the sources they draw on.
|
||||
- Contradictions are resolved or explicitly flagged — never left
|
||||
silently coexisting.
|
||||
- If the wiki has no confident answer, say so. Never file a
|
||||
low-confidence synthesis back as knowledge.
|
||||
- Git is the changelog. No separate log file.
|
||||
@@ -0,0 +1,106 @@
|
||||
# schema.yaml — single source of truth for artifact frontmatter.
|
||||
# Stage 1 of ADR-0004: scripts/validate.py checks generically against this
|
||||
# file. Extending the framework's metadata means editing THIS file, not code.
|
||||
# Agents: never invent fields or status values; propose a schema change.
|
||||
|
||||
version: 1
|
||||
|
||||
scope:
|
||||
# Files considered artifacts. Templates and raw sources are exempt.
|
||||
include:
|
||||
- "PROJECT.md"
|
||||
- "docs/**/*.md"
|
||||
exclude:
|
||||
- "**/template.md"
|
||||
- "docs/sources/**"
|
||||
- "vendor/**"
|
||||
# Files whose inline links are checked, but which need no frontmatter
|
||||
# (root-level prose: README, AGENTS, WORKFLOW, generated STATUS, ...).
|
||||
link_only:
|
||||
- "*.md"
|
||||
|
||||
# Frontmatter fields whose values are links. Values starting with
|
||||
# http://, https:// or mailto: are treated as external and only
|
||||
# format-checked; everything else must be a repo-root-relative path
|
||||
# to an existing file.
|
||||
link_fields: [related, sources, supersedes, superseded_by]
|
||||
|
||||
types:
|
||||
project:
|
||||
dir: "."
|
||||
filename: "^PROJECT\\.md$"
|
||||
required: [type, language, size_s_exception, purpose, audience]
|
||||
fields:
|
||||
language: { enum: [de, en] }
|
||||
size_s_exception: { kind: bool }
|
||||
purpose: { kind: str }
|
||||
audience: { kind: str }
|
||||
|
||||
adr:
|
||||
dir: "docs/adr"
|
||||
filename: "^\\d{4}-[a-z0-9-]+\\.md$"
|
||||
required: [type, id, status, date]
|
||||
fields:
|
||||
id: { pattern: "^\\d{4}$" }
|
||||
status: { enum: [proposed, accepted, superseded] }
|
||||
date: { kind: date }
|
||||
supersedes: { kind: link, nullable: true }
|
||||
superseded_by: { kind: link, nullable: true }
|
||||
related: { kind: links }
|
||||
rules:
|
||||
# status: superseded requires superseded_by to point at the successor.
|
||||
- superseded_requires_pointer
|
||||
|
||||
design:
|
||||
dir: "docs/design"
|
||||
filename: "^\\d{4}-\\d{2}-\\d{2}-[a-z0-9-]+\\.md$"
|
||||
required: [type, status, date, size]
|
||||
fields:
|
||||
status: { enum: [gate-1, gate-2, gate-3, gate-4, gate-5, done] }
|
||||
size: { enum: [L] }
|
||||
date: { kind: date }
|
||||
related: { kind: links }
|
||||
rules:
|
||||
# status: done if and only if the file lives under docs/design/done/.
|
||||
- done_iff_in_done_dir
|
||||
|
||||
aar:
|
||||
dir: "docs/aar"
|
||||
filename: "^\\d{4}-\\d{2}-\\d{2}-[a-z0-9-]+\\.md$"
|
||||
required: [type, status, date]
|
||||
fields:
|
||||
status: { enum: [open, harvested] }
|
||||
date: { kind: date }
|
||||
related: { kind: links }
|
||||
|
||||
issue:
|
||||
dir: "docs/issues"
|
||||
filename: "^\\d{4}-[a-z0-9-]+\\.md$"
|
||||
required: [type, id, status, created]
|
||||
fields:
|
||||
id: { pattern: "^\\d{4}$" }
|
||||
status: { enum: [open, in-progress, done, rejected] }
|
||||
created: { kind: date }
|
||||
related: { kind: links }
|
||||
|
||||
wiki-page:
|
||||
dir: "docs/wiki"
|
||||
filename: "^[a-z0-9-]+\\.md$"
|
||||
required: [type, area]
|
||||
fields:
|
||||
area:
|
||||
enum:
|
||||
- index
|
||||
- architecture
|
||||
- admin
|
||||
- deployment
|
||||
- user-guide
|
||||
- requirements
|
||||
- faq
|
||||
- stolpersteine
|
||||
sources: { kind: links }
|
||||
related: { kind: links }
|
||||
rules:
|
||||
# Pages other than the index should be linked from somewhere
|
||||
# (reported as WARNING, not error — see validate.py).
|
||||
- warn_if_orphan
|
||||
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_status.py — generate STATUS.md deterministically from frontmatter.
|
||||
|
||||
Writes STATUS.md (no timestamps — output depends only on repo content, so
|
||||
reruns are diff-clean). With --check, regenerates in memory and fails if
|
||||
the committed STATUS.md is stale; CI uses this mode.
|
||||
|
||||
Usage:
|
||||
python scripts/gen_status.py [repo-root] # write STATUS.md
|
||||
python scripts/gen_status.py --check [repo-root] # verify, exit 1 if stale
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError: # pragma: no cover
|
||||
sys.exit("gen_status.py needs PyYAML: pip install pyyaml")
|
||||
|
||||
H1_RE = re.compile(r"^#\s+(.*)$", re.M)
|
||||
|
||||
|
||||
def parse(path: Path):
|
||||
lines = path.read_text(encoding="utf-8").splitlines()
|
||||
if not lines or lines[0].strip() != "---":
|
||||
return None, ""
|
||||
for j in range(1, len(lines)):
|
||||
if lines[j].strip() == "---":
|
||||
meta = yaml.safe_load("\n".join(lines[1:j])) or {}
|
||||
body = "\n".join(lines[j + 1:])
|
||||
return meta, body
|
||||
return None, ""
|
||||
|
||||
|
||||
def title(body: str, fallback: str) -> str:
|
||||
match = H1_RE.search(body)
|
||||
return match.group(1).strip() if match else fallback
|
||||
|
||||
|
||||
def collect(root: Path, subdir: str, wanted_type: str):
|
||||
items = []
|
||||
base = root / subdir
|
||||
if not base.is_dir():
|
||||
return items
|
||||
for path in sorted(base.rglob("*.md")):
|
||||
if path.name == "template.md":
|
||||
continue
|
||||
meta, body = parse(path)
|
||||
if not isinstance(meta, dict) or meta.get("type") != wanted_type:
|
||||
continue
|
||||
rel = path.relative_to(root).as_posix()
|
||||
items.append((rel, meta, title(body, path.stem)))
|
||||
return items
|
||||
|
||||
|
||||
def render(root: Path) -> str:
|
||||
issues = collect(root, "docs/issues", "issue")
|
||||
designs = collect(root, "docs/design", "design")
|
||||
adrs = collect(root, "docs/adr", "adr")
|
||||
aars = collect(root, "docs/aar", "aar")
|
||||
|
||||
out: list[str] = []
|
||||
out.append("# STATUS")
|
||||
out.append("")
|
||||
out.append("<!-- Generated by scripts/gen_status.py — do not edit. -->")
|
||||
out.append("")
|
||||
|
||||
open_issues = [i for i in issues
|
||||
if i[1].get("status") in ("open", "in-progress")]
|
||||
closed = len(issues) - len(open_issues)
|
||||
out.append(f"## Issues ({len(open_issues)} open, {closed} closed)")
|
||||
out.append("")
|
||||
if open_issues:
|
||||
out.append("| Issue | Status | Title |")
|
||||
out.append("|---|---|---|")
|
||||
for rel, meta, name in open_issues:
|
||||
out.append(f"| [{meta.get('id', '?')}]({rel}) "
|
||||
f"| {meta.get('status')} | {name} |")
|
||||
else:
|
||||
out.append("_none open_")
|
||||
out.append("")
|
||||
|
||||
active = [d for d in designs if d[1].get("status") != "done"]
|
||||
out.append(f"## Active design docs ({len(active)})")
|
||||
out.append("")
|
||||
if active:
|
||||
out.append("| Design | Gate | Title |")
|
||||
out.append("|---|---|---|")
|
||||
for rel, meta, name in active:
|
||||
out.append(f"| [{Path(rel).stem}]({rel}) "
|
||||
f"| {meta.get('status')} | {name} |")
|
||||
else:
|
||||
out.append("_none active_")
|
||||
out.append("")
|
||||
|
||||
out.append(f"## ADRs ({len(adrs)})")
|
||||
out.append("")
|
||||
if adrs:
|
||||
out.append("| ADR | Status | Title |")
|
||||
out.append("|---|---|---|")
|
||||
for rel, meta, name in adrs:
|
||||
out.append(f"| [{meta.get('id', '?')}]({rel}) "
|
||||
f"| {meta.get('status')} | {name} |")
|
||||
else:
|
||||
out.append("_none_")
|
||||
out.append("")
|
||||
|
||||
open_aars = [a for a in aars if a[1].get("status") == "open"]
|
||||
out.append(f"## Open AARs ({len(open_aars)})")
|
||||
out.append("")
|
||||
if open_aars:
|
||||
for rel, _meta, name in open_aars:
|
||||
out.append(f"- [{name}]({rel})")
|
||||
else:
|
||||
out.append("_none — nothing awaiting harvest_")
|
||||
out.append("")
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = [a for a in sys.argv[1:] if a != "--check"]
|
||||
check = "--check" in sys.argv[1:]
|
||||
root = Path(args[0]) if args else Path.cwd()
|
||||
content = render(root)
|
||||
status = root / "STATUS.md"
|
||||
if check:
|
||||
current = status.read_text(encoding="utf-8") if status.is_file() else ""
|
||||
if current != content:
|
||||
print("gen_status --check: STATUS.md is stale — "
|
||||
"run scripts/gen_status.py and commit the result")
|
||||
return 1
|
||||
print("gen_status --check: STATUS.md is current")
|
||||
return 0
|
||||
status.write_text(content, encoding="utf-8", newline="\n")
|
||||
print(f"wrote {status}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env python3
|
||||
"""validate.py — deterministic artifact validation against schema.yaml.
|
||||
|
||||
Checks (errors, exit 1):
|
||||
* frontmatter present, parseable, `type` known
|
||||
* file location and filename match the type's rules
|
||||
* required fields, enums, patterns, dates
|
||||
* link fields: repo-root-relative targets exist (http/https/mailto skipped)
|
||||
* inline markdown links in bodies resolve (relative to the file)
|
||||
* per-type rules: superseded_requires_pointer, done_iff_in_done_dir
|
||||
|
||||
Warnings (exit 0):
|
||||
* wiki pages (except index) with no inbound link anywhere
|
||||
|
||||
Usage: python scripts/validate.py [repo-root]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import fnmatch
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError: # pragma: no cover
|
||||
sys.exit("validate.py needs PyYAML: pip install pyyaml")
|
||||
|
||||
DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$")
|
||||
INLINE_LINK_RE = re.compile(r"\]\(([^)\s]+)\)")
|
||||
HTML_SRC_RE = re.compile(r"(?:src|srcset)=\"([^\"]+)\"")
|
||||
EXTERNAL_PREFIXES = ("http://", "https://", "mailto:")
|
||||
|
||||
errors: list[str] = []
|
||||
warnings: list[str] = []
|
||||
|
||||
|
||||
def err(path: Path, msg: str) -> None:
|
||||
errors.append(f"ERROR {path}: {msg}")
|
||||
|
||||
|
||||
def warn(path: Path, msg: str) -> None:
|
||||
warnings.append(f"WARN {path}: {msg}")
|
||||
|
||||
|
||||
def parse_frontmatter(text: str):
|
||||
lines = text.splitlines()
|
||||
if not lines or lines[0].strip() != "---":
|
||||
return None, text
|
||||
for j in range(1, len(lines)):
|
||||
if lines[j].strip() == "---":
|
||||
fm = "\n".join(lines[1:j])
|
||||
body = "\n".join(lines[j + 1:])
|
||||
return yaml.safe_load(fm) or {}, body
|
||||
return None, text # unterminated
|
||||
|
||||
|
||||
def is_date(value) -> bool:
|
||||
if isinstance(value, datetime.date):
|
||||
return True
|
||||
return isinstance(value, str) and bool(DATE_RE.match(value))
|
||||
|
||||
|
||||
def as_links(value):
|
||||
"""Normalize a link field's value to a list of strings."""
|
||||
if value is None:
|
||||
return []
|
||||
if isinstance(value, str):
|
||||
return [value]
|
||||
if isinstance(value, list):
|
||||
return [v for v in value if isinstance(v, str)]
|
||||
return None # wrong shape
|
||||
|
||||
|
||||
def discover(root: Path, scope: dict) -> list[Path]:
|
||||
files: set[Path] = set()
|
||||
for pattern in scope.get("include", []):
|
||||
files.update(root.glob(pattern))
|
||||
result = []
|
||||
for f in sorted(files):
|
||||
rel = f.relative_to(root).as_posix()
|
||||
if any(fnmatch.fnmatch(rel, pat) for pat in scope.get("exclude", [])):
|
||||
continue
|
||||
if f.is_file():
|
||||
result.append(f)
|
||||
return result
|
||||
|
||||
|
||||
def check_fields(path: Path, meta: dict, spec: dict, root: Path) -> None:
|
||||
for field in spec.get("required", []):
|
||||
if field not in meta or meta[field] is None:
|
||||
err(path, f"missing required field '{field}'")
|
||||
for field, rule in (spec.get("fields") or {}).items():
|
||||
if field not in meta:
|
||||
continue
|
||||
value = meta[field]
|
||||
if value is None:
|
||||
if not rule.get("nullable"):
|
||||
# required-check already covers required fields;
|
||||
# a present-but-null optional field is fine unless typed link
|
||||
pass
|
||||
continue
|
||||
if "enum" in rule and value not in rule["enum"]:
|
||||
err(path, f"'{field}: {value}' not in enum {rule['enum']}")
|
||||
if "pattern" in rule and not re.match(rule["pattern"], str(value)):
|
||||
err(path, f"'{field}: {value}' does not match {rule['pattern']}")
|
||||
kind = rule.get("kind")
|
||||
if kind == "date" and not is_date(value):
|
||||
err(path, f"'{field}: {value}' is not a YYYY-MM-DD date")
|
||||
if kind == "bool" and not isinstance(value, bool):
|
||||
err(path, f"'{field}: {value}' is not a boolean")
|
||||
if kind == "str" and not isinstance(value, str):
|
||||
err(path, f"'{field}' must be a string")
|
||||
|
||||
|
||||
def check_links(path: Path, meta: dict, link_fields: list, root: Path,
|
||||
inbound: set) -> None:
|
||||
for field in link_fields:
|
||||
if field not in meta:
|
||||
continue
|
||||
links = as_links(meta[field])
|
||||
if links is None:
|
||||
err(path, f"'{field}' must be a string or list of strings")
|
||||
continue
|
||||
for link in links:
|
||||
if link.startswith(EXTERNAL_PREFIXES):
|
||||
continue
|
||||
target = (root / link)
|
||||
if not target.is_file():
|
||||
err(path, f"'{field}' link target missing: {link}")
|
||||
else:
|
||||
inbound.add(target.resolve())
|
||||
|
||||
|
||||
def check_body_links(path: Path, body: str, root: Path, inbound: set) -> None:
|
||||
# strip fenced code blocks and inline code spans so mermaid, code
|
||||
# samples, and literal link examples in backticks aren't scanned
|
||||
body = re.sub(r"```.*?```", "", body, flags=re.S)
|
||||
body = re.sub(r"`[^`\n]*`", "", body)
|
||||
candidates = [m.group(1) for m in INLINE_LINK_RE.finditer(body)]
|
||||
for raw in (m.group(1) for m in HTML_SRC_RE.finditer(body)):
|
||||
# srcset may list "path 2x, path2 1x" pairs — take each path token
|
||||
for part in raw.split(","):
|
||||
candidates.append(part.strip().split()[0])
|
||||
for link in candidates:
|
||||
if link.startswith(EXTERNAL_PREFIXES) or link.startswith("#"):
|
||||
continue
|
||||
link = link.split("#", 1)[0]
|
||||
if not link:
|
||||
continue
|
||||
target = (path.parent / link).resolve()
|
||||
if not target.is_file():
|
||||
err(path, f"inline link target missing: {link}")
|
||||
else:
|
||||
inbound.add(target)
|
||||
|
||||
|
||||
def apply_rules(path: Path, rel: str, meta: dict, spec: dict) -> None:
|
||||
for rule in spec.get("rules", []):
|
||||
if rule == "superseded_requires_pointer":
|
||||
if meta.get("status") == "superseded" and not meta.get("superseded_by"):
|
||||
err(path, "status 'superseded' requires 'superseded_by'")
|
||||
elif rule == "done_iff_in_done_dir":
|
||||
in_done = "/done/" in f"/{rel}"
|
||||
if (meta.get("status") == "done") != in_done:
|
||||
err(path, "status 'done' <-> file in docs/design/done/ mismatch")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
root = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.cwd()
|
||||
schema = yaml.safe_load((root / "schema.yaml").read_text(encoding="utf-8"))
|
||||
link_fields = schema.get("link_fields", [])
|
||||
types = schema.get("types", {})
|
||||
inbound: set = set()
|
||||
wiki_pages: list[tuple[Path, dict]] = []
|
||||
|
||||
# Root documents: inline links must resolve; no frontmatter required.
|
||||
for rel in schema.get("scope", {}).get("link_only", []):
|
||||
path = root / rel
|
||||
if not path.is_file():
|
||||
continue # e.g. STATUS.md before first generation
|
||||
text = path.read_text(encoding="utf-8")
|
||||
_meta, body = parse_frontmatter(text)
|
||||
check_body_links(path, body if _meta is not None else text,
|
||||
root, inbound)
|
||||
seen_ids: dict[tuple[str, str], Path] = {}
|
||||
artifacts = discover(root, schema.get("scope", {}))
|
||||
|
||||
for path in artifacts:
|
||||
rel = path.relative_to(root).as_posix()
|
||||
meta, body = parse_frontmatter(path.read_text(encoding="utf-8"))
|
||||
if meta is None:
|
||||
err(path, "missing or unterminated YAML frontmatter")
|
||||
continue
|
||||
if not isinstance(meta, dict) or "type" not in meta:
|
||||
err(path, "frontmatter has no 'type'")
|
||||
continue
|
||||
t = meta["type"]
|
||||
if t not in types:
|
||||
err(path, f"unknown type '{t}'")
|
||||
continue
|
||||
spec = types[t]
|
||||
expected_dir = spec.get("dir", ".")
|
||||
actual_dir = str(Path(rel).parent.as_posix())
|
||||
if expected_dir == ".":
|
||||
if actual_dir != ".":
|
||||
err(path, f"type '{t}' must live in repo root")
|
||||
elif not (actual_dir == expected_dir
|
||||
or actual_dir.startswith(expected_dir + "/")):
|
||||
err(path, f"type '{t}' must live under {expected_dir}/")
|
||||
fn_pattern = spec.get("filename")
|
||||
if fn_pattern and not re.match(fn_pattern, path.name):
|
||||
err(path, f"filename does not match {fn_pattern}")
|
||||
check_fields(path, meta, spec, root)
|
||||
if "id" in (spec.get("fields") or {}) and meta.get("id") is not None:
|
||||
artifact_id = str(meta["id"])
|
||||
if not path.name.startswith(f"{artifact_id}-"):
|
||||
err(path, f"id '{artifact_id}' does not match filename prefix")
|
||||
key = (t, artifact_id)
|
||||
if key in seen_ids:
|
||||
err(path, f"duplicate {t} id '{artifact_id}' "
|
||||
f"(also in {seen_ids[key].name})")
|
||||
else:
|
||||
seen_ids[key] = path
|
||||
check_links(path, meta, link_fields, root, inbound)
|
||||
check_body_links(path, body, root, inbound)
|
||||
apply_rules(path, rel, meta, spec)
|
||||
if t == "wiki-page" and meta.get("area") != "index":
|
||||
wiki_pages.append((path, meta))
|
||||
|
||||
# link-only files: inline links are checked, frontmatter not required
|
||||
already = {p.resolve() for p in artifacts}
|
||||
for pattern in schema.get("scope", {}).get("link_only", []):
|
||||
for path in sorted(root.glob(pattern)):
|
||||
if not path.is_file() or path.resolve() in already:
|
||||
continue
|
||||
meta, body = parse_frontmatter(path.read_text(encoding="utf-8"))
|
||||
if meta is None:
|
||||
body = path.read_text(encoding="utf-8")
|
||||
check_body_links(path, body, root, inbound)
|
||||
|
||||
for path, _meta in wiki_pages:
|
||||
if path.resolve() not in inbound:
|
||||
warn(path, "orphan wiki page — nothing links to it")
|
||||
|
||||
for line in errors + warnings:
|
||||
print(line)
|
||||
print(f"validate: {len(errors)} error(s), {len(warnings)} warning(s)")
|
||||
return 1 if errors else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
type: aar
|
||||
status: open # open | harvested
|
||||
date: YYYY-MM-DD
|
||||
related: [] # design docs, issues, ADRs involved
|
||||
---
|
||||
|
||||
<!-- Copy to docs/aar/YYYY-MM-DD-slug.md. Delete comments when filling in.
|
||||
Standalone AARs are for incidents and major deviations only —
|
||||
normal undertakings get their AAR as Gate 5 inside the design doc. -->
|
||||
|
||||
# AAR: Title
|
||||
|
||||
## What was planned / expected
|
||||
|
||||
## What happened
|
||||
|
||||
<!-- Facts and timeline, not blame. -->
|
||||
|
||||
## Why the difference
|
||||
|
||||
<!-- Root cause. For failures, name the routing class:
|
||||
intent issue / spec issue / code issue. -->
|
||||
|
||||
## Learnings
|
||||
|
||||
<!-- What future-you should know. Blunt beats polite. -->
|
||||
|
||||
## Actions
|
||||
|
||||
<!-- Concrete: wiki pages updated (FAQ, Stolpersteine) with links,
|
||||
framework issues opened, tests added. When all actions are done,
|
||||
set status: harvested. The refinement session walks all AARs
|
||||
still marked open. -->
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
type: adr
|
||||
id: "0000"
|
||||
status: proposed # proposed | accepted | superseded
|
||||
date: YYYY-MM-DD
|
||||
supersedes: null # path to older ADR, e.g. docs/adr/0002-old.md
|
||||
superseded_by: null # filled in on the OLD adr when a new one replaces it
|
||||
related: [] # optional: paths to design docs / issues
|
||||
---
|
||||
|
||||
<!-- Copy to docs/adr/NNNN-slug.md. Delete all comments when filling in. -->
|
||||
|
||||
# ADR-0000: Title
|
||||
|
||||
## Context
|
||||
|
||||
<!-- The situation and the forces at play. Constraints upfront:
|
||||
deadlines, scale, team knowledge, existing decisions. -->
|
||||
|
||||
## Options Considered
|
||||
|
||||
<!-- Name each option, even the one you lean toward. Pros/cons per
|
||||
option; a small dimension table (complexity, cost, maintenance,
|
||||
familiarity) where it helps. Keep proportional to the decision. -->
|
||||
|
||||
## Decision
|
||||
|
||||
<!-- The choice, in one or two sentences. -->
|
||||
|
||||
## Consequences
|
||||
|
||||
<!-- What becomes easier, what becomes harder, what we will need to
|
||||
revisit. Honest cons included. -->
|
||||
|
||||
<!-- Rules: an accepted ADR is never edited — write a new ADR that
|
||||
supersedes it and set superseded_by here. Lasting directional
|
||||
decisions only; feature-local choices belong in the design doc. -->
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
type: design
|
||||
status: gate-1 # gate-1 | gate-2 | gate-3 | gate-4 | gate-5 | done
|
||||
date: YYYY-MM-DD
|
||||
size: L # this template is for size L
|
||||
related: [] # issues, ADRs spawned or read
|
||||
---
|
||||
|
||||
<!-- Copy to docs/design/YYYY-MM-DD-slug.md. Delete comments when filling in.
|
||||
Fill ONE gate at a time; each gate ends with STOP — do not pre-fill
|
||||
later gates. Advance `status` only after human approval. -->
|
||||
|
||||
# Design: Title
|
||||
|
||||
## Gate 1 — Product
|
||||
|
||||
**Problem.** <!-- What user problem, for whom. -->
|
||||
|
||||
**Acceptance criterion.** <!-- Verifiable. A real number where one
|
||||
exists; otherwise a concretely checkable outcome. "Works" is not one. -->
|
||||
|
||||
**Non-goals.** <!-- What this deliberately does NOT do. The cheapest
|
||||
scope-creep brake there is. -->
|
||||
|
||||
**Announcement.** <!-- 3–5 sentences: what it is, who it's for, why
|
||||
it's good. Can't write it? The product isn't understood yet. -->
|
||||
|
||||
**Mockups.** <!-- Only if UI is involved: plain-HTML mockups, linked. -->
|
||||
|
||||
> **STOP — awaiting Gate 1 approval.**
|
||||
|
||||
## Gate 2 — Architecture
|
||||
|
||||
**Inputs read.** <!-- Which ADRs and AARs were read; one line each on
|
||||
why they matter here. -->
|
||||
|
||||
**System fit.** <!-- Endpoints, tables/schemas, query outlines,
|
||||
end-to-end flow as Mermaid. Against the actual codebase. -->
|
||||
|
||||
**Constraints.** <!-- Non-functional, proportional to the project:
|
||||
performance, security, operations, compatibility. "None relevant"
|
||||
is a valid answer — but say it. -->
|
||||
|
||||
**Options & trade-offs.** <!-- Where more than one viable way exists:
|
||||
name the options, pro/contra each, state the chosen one and WHY.
|
||||
This is the feature-local decision record. Only lasting, binding
|
||||
decisions graduate to an ADR below. -->
|
||||
|
||||
**New ADRs.** <!-- Lasting decisions discovered here → one ADR each,
|
||||
linked. None is a valid answer. -->
|
||||
|
||||
> **STOP — awaiting Gate 2 approval.**
|
||||
|
||||
## Gate 3 — Program Design
|
||||
|
||||
**Files.** <!-- Exact paths, new and touched. -->
|
||||
|
||||
**Signatures.** <!-- Types and method signatures, no bodies. -->
|
||||
|
||||
**Call stack.** <!-- For the main flow(s). -->
|
||||
|
||||
**Test assertions.** <!-- What the tests will assert. -->
|
||||
|
||||
**Boundaries — DO NOT CHANGE.** <!-- Explicit list. -->
|
||||
|
||||
**Shakiest calls.** <!-- The decisions you are least confident about. -->
|
||||
|
||||
> **STOP — awaiting Gate 3 approval.**
|
||||
|
||||
## Gate 4 — Vertical Slices
|
||||
|
||||
<!-- Slice 1 is the tracer bullet: thin end-to-end, runs with mocks.
|
||||
Then real logic, one testable slice at a time. Per task:
|
||||
files / action / verify / done. After each slice: evidence,
|
||||
status, STOP. -->
|
||||
|
||||
### Slice 1 — Tracer bullet
|
||||
- [ ] Task: … — files: … — action: … — verify: … — done: …
|
||||
|
||||
**Evidence:** <!-- command output, test run, screenshot ref -->
|
||||
**Status:** <!-- DONE | DONE_WITH_CONCERNS | NEEDS_CONTEXT | BLOCKED -->
|
||||
|
||||
> **STOP — slice review.**
|
||||
|
||||
### Slice 2 — …
|
||||
|
||||
### Handoff
|
||||
|
||||
<!-- The single place session state lives. Overwrite on every handoff;
|
||||
git keeps the history.
|
||||
Done slices: …
|
||||
Open decisions: …
|
||||
Next step: … -->
|
||||
|
||||
## Gate 5 — Closeout (AAR)
|
||||
|
||||
**Planned vs. actual.** <!-- What was planned, what happened. -->
|
||||
|
||||
**Why the difference.** <!-- Root causes, honestly. -->
|
||||
|
||||
**Learnings.** <!-- What future-you should know. -->
|
||||
|
||||
**Harvested.** <!-- Wiki pages updated (FAQ, Stolpersteine, …) with
|
||||
links; framework issues opened, if a rule was missing or wrong. -->
|
||||
|
||||
**Open uncertainties.** <!-- Session-handoff answers to: "Which choices
|
||||
did I make that I'm least confident about?" -->
|
||||
|
||||
<!-- After approval: set status: done, move this file to
|
||||
docs/design/done/, run gen_status.py. -->
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
type: issue
|
||||
id: "0000"
|
||||
status: open # open | in-progress | done | rejected
|
||||
created: YYYY-MM-DD
|
||||
related: [] # design docs, ADRs, other issues
|
||||
---
|
||||
|
||||
<!-- Copy to docs/issues/NNNN-slug.md. Delete comments when filling in. -->
|
||||
|
||||
# Issue-0000: Title
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
<!-- What's wrong or missing, and why it matters. One paragraph. -->
|
||||
|
||||
## Acceptance
|
||||
|
||||
<!-- When is this issue done? Verifiable, like every other criterion
|
||||
in this framework. -->
|
||||
|
||||
## Notes
|
||||
|
||||
<!-- Optional: context, links, findings gathered along the way.
|
||||
Rules: status is the single source of truth and lives here in the
|
||||
frontmatter — STATUS.md is generated, never edited. An issue that
|
||||
starts real work links its design doc in `related`. Closed means
|
||||
status: done (or rejected, with a one-line reason in Notes) —
|
||||
the file stays; git is the history. -->
|
||||
@@ -1,3 +1,9 @@
|
||||
---
|
||||
type: wiki-page
|
||||
area: admin
|
||||
related: []
|
||||
---
|
||||
|
||||
# CFGMON
|
||||
|
||||
Monitoring-Stack, Gitea und der Reverse Proxy für alles Öffentliche.
|
||||
@@ -136,22 +142,23 @@ Gitea selbst, gitops-Repo als Flux-Source, Issues/Wiki/dieses Repo, der
|
||||
API-Token für Issue-Verwaltung, das Gitea-Backup-Script (CFGMON-09).
|
||||
|
||||
*(Stand der Analyse 2026-07-31. Issues und dieses Repo sind seitdem doch
|
||||
umgezogen — [ADR-0002](../decisions/0002-issues-und-management-ins-lab.md) —,
|
||||
umgezogen — [ADR-0002](../../adr/0002-issues-und-management-ins-lab.md) —,
|
||||
das Repo dabei von `Backlogs` zu `management` umgewidmet
|
||||
[ADR-0005](../decisions/0005-pm-framework-kanban.md). „Nicht rückbaubar" galt für
|
||||
[ADR-0005](../../adr/0005-pm-framework-kanban.md). „Nicht rückbaubar" galt für
|
||||
den damaligen Rückbau der Gitea-CI, nicht auf Dauer.)*
|
||||
|
||||
Betroffene Issues (werden bei der GitLab-Migrations-Planung umformuliert):
|
||||
[ThreadNet-Web#2](https://rohana.axion1337.de/sorb/ThreadNet-Web/issues/2),
|
||||
[threadnet-call#1](https://rohana.axion1337.de/sorb/threadnet-call/issues/1).
|
||||
`ThreadNet-Web#2` (Gitea-Zählung, Tracker stillgelegt — verbindlich: Migrations-Fußtext im GitLab-Issue),
|
||||
`threadnet-call#1` (Gitea-Zählung, Tracker stillgelegt).
|
||||
|
||||
**Nächster Schritt:** die drei manuellen Schritte oben, dann → erledigt.
|
||||
**Nächster Schritt:** die drei manuellen Schritte oben, dann → erledigt. Verfolgt als
|
||||
[CFGMON-11 (#34)](../../issues/0034-cfgmon-11-gitea-ci-rueckbau-abschliessen.md).
|
||||
|
||||
## CFGMON-13 — Absender-Design für Release-/CVE-Meldungen: eigener Bot?
|
||||
|
||||
**Status:** entschieden (2026-08-01, sorb) — **gleicher Bot (`@alerts`), eigener Raum**
|
||||
`!YRJvcEbVXtRlUIkNld:axion1337.chat`. Umsetzungsplan inkl. CVE-Metriken/Grafana/
|
||||
Alertmanager-Routing: [gitops#47](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/47).
|
||||
Alertmanager-Routing: [gitops#45 auf git.lab](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/45) (ehemals Gitea-gitops#47, Tracker stillgelegt).
|
||||
release-watch ist bereits auf den Raum vorbereitet (Env `MATRIX_RELEASE_ROOM_ID`,
|
||||
Fallback Alerts-Raum). ⬜ Rest: `@alerts` in den Raum **einladen** (Join wurde als
|
||||
restricted abgelehnt — sorb), dann Deploy.
|
||||
@@ -173,7 +180,7 @@ neuen Absender bauen.
|
||||
|
||||
## CFGMON-12 — Gitea-Projektmetadaten nach GitLab umziehen/integrieren
|
||||
|
||||
**Status:** abgelöst durch [gitops#48](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/48) (2026-08-01, sorb: HOHE Priorität — vollständige Issue-Migration + zentrale Gruppen-Roadmap; Plan-Skizze und die offene Erreichbarkeits-Entscheidung git.lab-only vs. extern stehen dort)
|
||||
**Status:** abgelöst durch [gitops#46 auf git.lab](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/46) (ehemals Gitea-gitops#48, Tracker stillgelegt) (2026-08-01, sorb: HOHE Priorität — vollständige Issue-Migration + zentrale Gruppen-Roadmap; Plan-Skizze und die offene Erreichbarkeits-Entscheidung git.lab-only vs. extern stehen dort)
|
||||
|
||||
✅ **Umgesetzt am 2026-08-01/02**: Die Migration ist durch — 62 Issues liegen auf
|
||||
git.lab, die Gitea-Issues sind geschlossen und tragen einen Migrations-Fußtext.
|
||||
@@ -231,7 +238,7 @@ ohne Swap, trägt daneben Gitea/Traefik/Monitoring) kann das strukturell nicht l
|
||||
**Verworfen statt gefixt**: Limit-Anhebung/Swap wird bewusst nicht weiterverfolgt —
|
||||
Build-CI zieht ins Homelab-GitLab um (siehe
|
||||
[CFGMON-11](#cfgmon-11--gitea-ci-rückbau-nach-gitlab-umzug)), CFGMON bleibt bei leichten
|
||||
Jobs. Issue-Seite: [threadnet-call#1](https://rohana.axion1337.de/sorb/threadnet-call/issues/1).
|
||||
Jobs. Issue-Seite: `threadnet-call#1` (Gitea-Zählung, Tracker stillgelegt).
|
||||
|
||||
### CFGMON-02 — Traefik, Gitea, cAdvisor und Runner unter IaC gebracht · erledigt 2026-07-30
|
||||
|
||||
@@ -295,5 +302,5 @@ existiert und wo einer laufen sollte, noch offen sei. Beides falsch — ein Runn
|
||||
(`builder-1`) läuft bereits, auf CFGMON, als Teil von `thread-net-git`s `rework/stack`-
|
||||
Branch, mit gezielt für Electron-Builds eingerichteten Labels. Details siehe
|
||||
[CFGMON-02](#cfgmon-02--traefik-gitea-cadvisor-und-runner-unter-iac-gebracht--erledigt-2026-07-30) — hier
|
||||
nicht dupliziert. [gitops#33](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/33)
|
||||
nicht dupliziert. `gitops#33` (Gitea-Zählung, Tracker stillgelegt)
|
||||
(dieselbe falsche Prämisse) entsprechend korrigiert/geschlossen.
|
||||
@@ -1,3 +1,9 @@
|
||||
---
|
||||
type: wiki-page
|
||||
area: admin
|
||||
related: []
|
||||
---
|
||||
|
||||
# game
|
||||
|
||||
Pterodactyl- / Gameserver-Host.
|
||||
@@ -1,3 +1,9 @@
|
||||
---
|
||||
type: wiki-page
|
||||
area: admin
|
||||
related: []
|
||||
---
|
||||
|
||||
# matrix
|
||||
|
||||
Matrix-Homeserver (Element Server Suite / Synapse) + K3s-Single-Node-Cluster, GitOps-verwaltet.
|
||||
@@ -49,7 +55,7 @@ Firewall-Drift; extern war 9100 nie freigegeben (und soll es nicht sein).
|
||||
**Fix (gitops `228807f`, Weg A aus gitops#45):** HelmRelease + Alloy-Scrape entfernt,
|
||||
Flux hat gepruned — DaemonSet/Service/Pod sind weg, Host-Metriken kommen unverändert
|
||||
vom systemd-Exporter. Volle Diagnose:
|
||||
[gitops#45](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/45).
|
||||
`gitops#45` (Gitea-Zählung, Tracker stillgelegt — verbindlich: Migrations-Fußtext im GitLab-Issue).
|
||||
|
||||
<details><summary>Ursprünglicher Befund (CFGMON-Session, vor der Host-Prüfung)</summary>
|
||||
|
||||
@@ -171,7 +177,7 @@ laufen ausschließlich über Authentik (OIDC, `auth.axion1337.chat`) und Einladu
|
||||
Der komplette IONOS-Mail-Satz auf `matrix.axion1337.de` ist damit **funktional unnötig** —
|
||||
dieselbe Härtung wie bei `selendis` anwenden (Null-MX, `v=spf1 -all`, `_dmarc p=reject`),
|
||||
`autodiscover.matrix` kann ebenfalls weg. Damit ist auch
|
||||
[ZONE-02](../shared/zone-axion1337.md) an dieser Stelle entblockt.
|
||||
[ZONE-02](../architecture/zone-axion1337.md) an dieser Stelle entblockt.
|
||||
|
||||
**Separat davon** (andere Domain-Ebene, kein Widerspruch): auf diesem Host läuft seit
|
||||
2026-07-30 ein eigener Mailversand für Host-Wartungsbenachrichtigungen
|
||||
@@ -198,7 +204,7 @@ nichts mehr zu tun. Ob Prometheus/Loki auf CFGMON zusätzlich öffentlich erreic
|
||||
|
||||
Neuer, eigenständiger Mechanismus auf diesem Host, außerhalb von Flux/GitOps (Details:
|
||||
`docs/deployment-guides/07-host-maintenance-notifications.md` im gitops-Repo,
|
||||
[Issue #24](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/24)):
|
||||
`gitops#24` (Gitea-Zählung, Tracker stillgelegt)):
|
||||
`unattended-upgrades` war bereits aktiv, neu ergänzt ist ein systemd-Timer
|
||||
(`maintenance-notify.timer`, fest 05:00 Uhr, vor dem 06:00-07:00-Update-Fenster), der bei
|
||||
anstehenden Paket-Updates per Mail **und** Matrix (Thread-Reply im `wartung`-Raum)
|
||||
@@ -1,3 +1,9 @@
|
||||
---
|
||||
type: wiki-page
|
||||
area: admin
|
||||
related: []
|
||||
---
|
||||
|
||||
# Overmind
|
||||
|
||||
Homelab-Host: GitLab (Dokploy-verwaltet) + CI-Runner. **Nur im Lab erreichbar** —
|
||||
@@ -36,14 +42,14 @@ ein gleichnamiges Repo mit anderem Stand.
|
||||
|
||||
Gitea bleibt: Flux-Source (via Mirror beliefert), Registry, Packages.
|
||||
**Issues nicht mehr** — die sind am 2026-08-01/02 nach git.lab gewandert
|
||||
([ADR-0002](../decisions/0002-issues-und-management-ins-lab.md)). Die letzte Ausnahme,
|
||||
([ADR-0002](../../adr/0002-issues-und-management-ins-lab.md)). Die letzte Ausnahme,
|
||||
die Deploy-Übergabe-Issues auf dem Gitea-Tracker `sorb/management`, ist am 2026-08-02
|
||||
mit LABNET-03 zurückgebaut: beide umgezogen (#25, #26), der Tracker ist leer.
|
||||
**Ohne Ausnahme: Issues leben auf git.lab.**
|
||||
|
||||
*(Bis 2026-08-01 stand hier „Backlogs (dieses Repo, ungespiegelt)" — das Repo heißt
|
||||
seit der Umwidmung zum Management-Repo `management` und wird seither gespiegelt,
|
||||
[ADR-0005](../decisions/0005-pm-framework-kanban.md).)*
|
||||
[ADR-0005](../../adr/0005-pm-framework-kanban.md).)*
|
||||
|
||||
## OVERMIND-01 — GitLab-Container-Registry aktivieren, Images nach Konsument sortieren
|
||||
|
||||
@@ -90,7 +96,8 @@ vorhanden, Runbook referenziert `stable`.
|
||||
**Nächster Schritt:** `element-desktop-build` von rohana in die Lab-Registry umziehen
|
||||
(ThreadNet-Web-CI: `desktop_image`-Push-Ziel + `desktop_linux`-Image-Referenz) — bewusst
|
||||
zurückgestellt, bis kein Auto-Job das alte Image parallel referenziert (Reihenfolge:
|
||||
erst neues Image bauen, dann Referenz umstellen).
|
||||
erst neues Image bauen, dann Referenz umstellen). Verfolgt als
|
||||
[OVERMIND-01 (#33)](../../issues/0033-overmind-01-element-desktop-build-lab-registry.md).
|
||||
|
||||
## OVERMIND-02 — Host-Ausfall 2026-07-31 ~19:15 lokal (NIC-Hang, Fix aktiv)
|
||||
|
||||
@@ -112,6 +119,7 @@ jedem Boot). Temporäre sudoers-Freigabe danach wieder entfernt.
|
||||
- ~~NIC-/BIOS-Firmware-Update 2.4.0.0 → 2.5.2.0~~ **erledigt** (Wartungsfenster
|
||||
2026-08-01, durch sorb)
|
||||
- Falls der Hang trotz EEE-off + neuer Firmware wiederkehrt: gezielter ASPM-Fix
|
||||
— Beobachtung verfolgt als [OVERMIND-02 (#4)](../../issues/0004-overmind-02-e1000e-nic-hang-beobachtung-nach.md)
|
||||
statt globalem Kernel-Parameter
|
||||
|
||||
**Zeitleiste (lokal, UTC+2):**
|
||||
@@ -1,7 +1,13 @@
|
||||
---
|
||||
type: wiki-page
|
||||
area: admin
|
||||
related: []
|
||||
---
|
||||
|
||||
# Refinement und Retro — die Termine des Frameworks
|
||||
|
||||
Kanban braucht wenige, aber verlässliche Termine, sonst verkommt das Board zur
|
||||
Ablage. Festgelegt in [ADR-0005](../decisions/0005-pm-framework-kanban.md); hier
|
||||
Ablage. Festgelegt in [ADR-0005](../../adr/0005-pm-framework-kanban.md); hier
|
||||
steht, wie sie ablaufen.
|
||||
|
||||
## Termine (festgelegt im Struktur-Workshop, 2026-08-06)
|
||||
@@ -48,13 +54,13 @@ Drei Fragen, mehr nicht:
|
||||
Grundlage sind die AARs des Monats — sie sind die Retro-Vorbereitung, nicht ihr
|
||||
Ersatz.
|
||||
|
||||
Ergebnisse werden unter [`retro/`](retro/) abgelegt, eine Datei je Termin. Die
|
||||
erste: [2026-08-09](retro/2026-08-09.md).
|
||||
Ergebnisse werden unter [`docs/sources/protokolle/`](../../sources/protokolle/retro-2026-08-09.md) abgelegt, eine Datei je Termin. Die
|
||||
erste: [2026-08-09](../../sources/protokolle/retro-2026-08-09.md).
|
||||
|
||||
## AAR (anlassbezogen)
|
||||
|
||||
Nach jedem Deploy mit Übergabe und nach jedem Incident, Vorlage in
|
||||
[aar-vorlage.md](aar-vorlage.md). Ein AAR ist keine Chronik, sondern ein
|
||||
[docs/aar/template.md](../../aar/template.md). Ein AAR ist keine Chronik, sondern ein
|
||||
Wissensspeicher: Was war das Ergebnis, welche Befunde, was hat die Eingrenzung
|
||||
ermöglicht, welche Lehren, was bleibt offen. **Offene Punkte aus einem AAR werden
|
||||
im selben Zug zu Issues** — sonst versacken sie in der Prosa (real passiert am
|
||||
@@ -85,10 +91,10 @@ wertlos.
|
||||
Mehrere Claude-Sessions arbeiten parallel (Mac-Session, Host-Sessions auf CFGMON
|
||||
und Overmind). Für sie gilt:
|
||||
|
||||
- Die **kanonischen Arbeitskonventionen** stehen in [`CLAUDE.md`](../CLAUDE.md) und
|
||||
- Die **kanonischen Arbeitskonventionen** stehen in [`CLAUDE.md`](../../../AGENTS.md) und
|
||||
sind über den Gitea-Mirror von überall lesbar.
|
||||
- Arbeit zwischen Sessions läuft über das
|
||||
[Deploy-Übergabe-Verfahren](deploy-uebergabe.md) — Auftrag, Meldung, Protokoll
|
||||
[Deploy-Übergabe-Verfahren](../deployment/deploy-uebergabe.md) — Auftrag, Meldung, Protokoll
|
||||
im Issue, nicht im Chat.
|
||||
- Was eine Session lernt, gehört ins Repo (AAR/ADR/Doku), nicht nur in ihr
|
||||
Gedächtnis — Sessions gehen verloren, Repos nicht.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user