Author SHA1 Message Date
Thore CimbalandClaude Fable 5 28e1843e8d docs: add the neckbeard handoff document (field-test results)
Frozen handoff record for the upstream repo, English per neckbeard's
own artifact convention: the completed first size-L run (the ADR-0006
v1.0.0 trigger), eleven feedback items each with field evidence and
reference implementations, and a where-to-look table. Issue 0040 now
points at it; go-live item 1 in issue 0042 is ticked off by this push.
Size S under the granted exception - one deliverable, no design
decisions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 12:00:00 +00:00
Thore CimbalandClaude Fable 5 c481165ab4 docs: Gate 5 closeout - AAR, harvest, design doc done
The design doc closes with its AAR (planned/actual/why/learnings, the
six acceptance criteria checked off 6/6, the session's shakiest calls
named) and moves to docs/design/done/ with status done. Harvest: a
stolpersteine wiki page distilled from the AAR (hex is not a git SHA,
TZ on the git process, python floor, anonymous Gitea negatives,
negative tests, directory links), and the neckbeard feedback list
becomes issue 0040 - a deliberate separate act, per the design's
non-goals. Operational follow-up is issues 0041 (refine imported
wartegrund) and 0042 (go-live: push, first mirror run, CI schedule,
milestone for gitops#61). Final chain green: validate 0/0 over 36 open
issues, gen_status --check current, drift 0, prosa 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 12:00:00 +00:00
Thore CimbalandClaude Fable 5 865d761fb0 feat: slice 5 - components declared, group checks live, mirror dry-run
Gate 4, slice 5: eight component declarations under docs/components/
(filename = canonical slug, F-008 answered by construction; staged
dormancy of thread-net-git/threadnet-operating finally representable,
game-operating/gameserver as external with their field-test caveats),
five pointer-rollout follow-up issues (0035-0039, ADR-0013),
gruppenpruefung.py joins the stillstandspruefung family (runtime group
list vs declarations, pointer presence, group-wide milestone/priority
duty, issue drift, git hygiene since the 2026-08-07 rule boundary,
bot exception per ADR-0009) with its own scheduled CI job, and
spiegel_issues.py mirrors repo to GitLab (title, state, milestone,
priority, due, status label only - never descriptions, never
backwards, dry-run by default, GitLab-only issues are reported and
never auto-closed).

Verified - all four pattern demos fire (acceptance criterion 5, 4/4):
A) old CLAUDE.md claims M1-M4 while the frozen export knows M5;
B) hygiene over full clone history finds 222 real-clock commits by
own identities (matches the frozen Session-1 numbers per repo);
C) covered in slices 3/4 (five task blocks, now 0);
D) covered in slice 3 (orphaned SHA citations, now resolved/curated).
Live run (read-only): exactly the four missing pointers (F-011) and
gitops#61 without milestone as red findings, zero drift on all 26
mirrored issues, group list consistent. Mirror dry-run plans 7
creations, 0 updates, wrote nothing. Offline chain green: validate
0/0, gen_status --check current, drift 0, prosa 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 12:00:00 +00:00
Thore CimbalandClaude Fable 5 c23bb54a92 feat: slice 4 - the open management issues live in the repo
Gate 4, slice 4: 26 open management issues imported from live git.lab
(read-only, descriptions included as authorized; GitLab iid = file id,
labels/milestone/priority/due/host/area mapped into frontmatter, the
import aborts instead of inventing a missing milestone or priority).
Two new issues close the F-004 gap where work was really still open
(0033 OVERMIND-01, 0034 CFGMON-11 incl. the plaintext npm-token
rotation); CFGMON-12/13 already route to verified git.lab issues,
MATRIX-05 is done and needs none (agreed with sorb). The three wiki
task blocks now reference their issues, roadmap.md hands all counts to
the generated STATUS.md and states M1-M5 per ADR-0010 (closing F-001
in the canonical prose), pruefe_prosa joins the CI validate job, and
the import protocol under docs/sources/migration/ records every
intervention into imported text.

Verified: validate 0/0 over 29 issue files, gen_status --check current
(distribution line M1 9 - M2 17 - M4 2 plus per-issue milestone and
priority), pruefe_prosa 0 errors with clones and 0 errors/15 unchecked
citations in offline CI mode, drift check green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 12:00:00 +00:00
Thore CimbalandClaude Fable 5 92b448fe30 feat: slice 3 - wiki, sources and AARs in their neckbeard homes
Gate 4, slice 3: verfahren/, hosts/, vision/ and shared/ moved via git
mv - six AARs to docs/aar/ (four harvested by the 2026-08-09 retro,
two open), procedures and host knowledge to docs/wiki/ (admin,
deployment, architecture, new area vision), the retro protocol and the
commit mapping table to docs/sources/ (protokolle/, migration/). New:
the wiki index linking every page, and the mirror-topology page
carrying the why-two-places reasoning verbatim from the old CLAUDE.md
(F-013 preserved). All moved-path references retargeted; the link
checker drove the sweep to zero.

pruefe_prosa.py added (pattern C+D): SHA citations resolve via repo,
mapping table, optional component clones or a curated exemption list
(documented dead Gitea-force-push commits, a vendor-repo tag, an
Authentik uid that is hex but no git SHA, the external neckbeard
reference); wiki task prose without an issue reference errors, with a
visible pragma for deliberate checklists; the dead-tracker denylist
now covers every mirrored repo's retired Gitea tracker (F-005) - two
links re-verified against live GitLab titles and retargeted, five
defused into honest historical citations.

Verified: validate 0/0, gen_status --check current, drift 0. Demo on
the pre-migration state fires 6 findings (3 orphaned SHAs, 3 task
blocks); on the current tree exactly the 3 F-004 task blocks remain -
they turn green in slice 4 when the issues exist, which is why
pruefe_prosa joins CI only then.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 12:00:00 +00:00
Thore CimbalandClaude Fable 5 70e81e2ff1 feat: slice 2 - all eleven decisions ported to docs/adr
Gate 4, slice 2: decisions/0001-0011 moved via git mv with schema
frontmatter prepended (status and date taken from each body's own
Status line - 0007 stays proposed, its decision is open in #20; bodies
unchanged except relative links gaining one directory level). The old
scheme's README and template retire - their rules already live in
AGENTS.md section 6 and the neckbeard ADR template. Every reference to
decisions/ across the tree retargeted (root files, not-yet-moved
verfahren/hosts/shared files, design doc and session ADR frontmatter).

Verified: validate 0 errors (11 ported + 2 session ADRs + duplicate-id
guard), gen_status --check current with all 13 ADRs listed, drift
check 0 findings, negative test shows a cloned id 0012 firing the
duplicate check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 12:00:00 +00:00
Thore CimbalandClaude Fable 5 e36ed337a7 feat: slice 1 - the neckbeard framework chain runs end to end
Tracer bullet of the migration design (Gate 4, slice 1): pinned v0.1.1
baseline under docs/sources/upstream/ with provenance note, the
Karpathy block moved verbatim to docs/sources/regelwerk/ (standing
rule mapped onto the sources read-only mechanism), AGENTS.md assembled
from the byte-true upstream sections plus the project section 6
(group rules condensed from the old CLAUDE.md), CLAUDE.md reduced to
the upstream pointer, WORKFLOW.md and all four templates copied,
schema.yaml extended (issue milestone/priority/status columns,
component type, wiki area vision - all flagged in the header),
validate.py and gen_status.py forked with marked extensions,
pruefe_upstream_drift.py added, STATUS.md generated, CI gains the
offline validate job, README directory link defused.

Verified: validate 0 errors 0 warnings (the three pre-existing
directory-link errors are gone), gen_status --check current,
drift check 0 findings, baseline byte-identical to the reference
checkout (10/10 files), four negative tests fire (WIP limit 3x
in-progress, waiting without wartegrund, component slug mismatch,
single-byte drift in WORKFLOW.md). gen_status needs Python >= 3.10
locally (write_text newline) - noted for the design AAR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 12:00:00 +00:00
Thore CimbalandClaude Fable 5 5e46372ea8 docs: rebase onto moved main, renumber session ADRs to 0012/0013
Reality moved after the Gate-3 approval (flagged by the human, verified
read-only): main gained decisions/0011 plus a new AAR, gitops gained two
commits, and the live backlog shows gitops#61 without a milestone - the
first real break of the 100% milestone discipline. Session ADRs
renumbered to avoid the id collision, counts updated (11 old ADRs, 6
AARs), gruppenpruefung gains the group-wide milestone/priority duty
check backed by that real case. Addendum in the design doc records all
of it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 12:00:00 +00:00
Thore CimbalandClaude Fable 5 0cce6f641c docs: design doc Gate 3 (program design)
Complete target file map, exact schema extensions, script signatures
without bodies, CI flow, per-check assertions including the four
pattern demonstrations and negative tests, DO NOT CHANGE boundaries,
and the six shakiest calls named.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 20:45:18 +02:00
Thore CimbalandClaude Fable 5 bff1ca4477 docs: close Gate 2 - sources taxonomy, upstream drift check, ADRs accepted
Amendments decided with the human at the Gate-2 STOP: docs/sources/
gets a by-source-type taxonomy (regelwerk/upstream/protokolle/
migration, proposed by sorb), the pinned v0.1.1 originals become a
byte-compare baseline against silent framework-file rewrites, AGENTS.md
carries the change-only-with-sorb rule forward, and the issue import
may read descriptions via the token (read-only). ADR-0011 and ADR-0012
flipped to accepted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 20:45:18 +02:00
Thore CimbalandClaude Fable 5 6b8869f160 docs: design doc Gate 2 (architecture) with ADR-0011 and ADR-0012
Two-way harvest as mandated by the Session-1 handoff: failure patterns
of both approaches tabled with the mechanism that closes each, all
seven neckbeard gaps dispositioned (plus two new ones found this
session), and the old approach's proven value folded into the target
architecture. Two directional decisions filed as proposed ADRs: issues
live in-repo with GitLab as a deterministically mirrored view (0011),
group rules canonical here with pointer components and a checkable
components artifact (0012). Migration map, check architecture split
offline/runtime, constraints, upstream feedback candidates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 20:45:18 +02:00
Thore CimbalandClaude Fable 5 2daadb8ba6 docs: add migration design doc, Gate 1 (product)
Problem statement built on the four drift patterns from the Session-1
field test, six numeric acceptance criteria, non-goals (no history
rewrite, no push, no component rollout, no forge-state destruction),
announcement paragraph. Gates 2-5 deliberately not pre-filled, per
WORKFLOW.md. Frontmatter validates against neckbeard v0.1.1 schema
with 0 errors for this file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 20:45:18 +02:00
Thore CimbalandClaude Fable 5 d47c2c4efb docs: initialize PROJECT.md (neckbeard Gate 0)
Answers recorded from the Gate 0 questions, asked and confirmed by the
human on 2026-08-11: language de, size-S exception granted, purpose and
audience as stated in the frontmatter. Validated against neckbeard
v0.1.1 schema.yaml (823a08c) with 0 errors, 0 warnings; the framework
files themselves enter this repo only after the two-way harvest mandated
by the Session-1 handoff.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 20:45:18 +02:00
Thore Cimbal dbc309dfd6 docs(adr): 0011 - reject localpart collisions in provisioning
Records on_conflict: fail as standing policy (identity provisioning never links
a new upstream identity to an existing local account), the residual prompt-stage
uniqueness check, and the SOPS-secret-needs-restart rule. Decided by sorb.
2026-08-11 12:00:00 +00:00
Thore Cimbal be92ab180d docs(aar): note the on_conflict fix needed a MAS restart to go live
The SOPS secret updated via Flux but MAS kept the old config in memory until a
rollout restart. Records committed != deployed != active for the security fix.
2026-08-11 12:00:00 +00:00
Thore Cimbal ff0cf0d8bd docs: AAR for the @apo call failure - missing Synapse profiles row
Root cause proven end to end: a pre-Authentik account that lost its profiles
row (deactivate clears it, reactivate does not recreate it) crashes the
displayname write path, so it never gets a display name and the Element Call
widget never initialises. Fixed with a cross-checked INSERT; open_id_tokens
went 0 -> 6 and the call joined. Records the ruled-out suspects, what led to
the solution, and the lessons - chief among them: compare old accounts against
freshly provisioned ones, and reproduce in a cleartext room before blaming
crypto. Also notes the account-takeover finding (gitops#61) surfaced along the
way.
2026-08-11 12:00:00 +00:00
123 changed files with 5858 additions and 404 deletions
+36
View File
@@ -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
+179
View File
@@ -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 15, 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 15
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 (M1M5) 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
View File
@@ -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 M1M4,
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.
M1M4 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
View File
@@ -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.
+7 -7
View File
@@ -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
+73
View File
@@ -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
View File
@@ -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 15 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 15 (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 (35 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.
-19
View 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.
-19
View File
@@ -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? (25 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.
@@ -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
@@ -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
@@ -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
@@ -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.
+34
View File
@@ -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. -->
@@ -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
@@ -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
@@ -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 17 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
@@ -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
@@ -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
@@ -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).
@@ -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.
@@ -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, M1M5)
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.
+37
View File
@@ -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. -->
+14
View File
@@ -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).
+14
View File
@@ -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).
+14
View File
@@ -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).
+14
View File
@@ -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).
+14
View File
@@ -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).
+14
View File
@@ -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.
+14
View File
@@ -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.
+14
View File
@@ -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
„M1M4" (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 (M1M5).
5. **Muster → Mechanismus:** für jedes Driftmuster AD 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 15,
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 15 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 §15 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` („M1M4") ↔ 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 §15 / 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/M1M5): 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 132.
- 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`
(§15 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 05 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 M1M5; (5) 4/4 Muster-Demos gefeuert
(A: „M1M4"↔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)
[00350039](../../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).
+110
View File
@@ -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.** <!-- 35 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 ~1525 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 00330042 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.
+29
View File
@@ -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. -->
+123
View File
@@ -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 132 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 15, 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 15, 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 15 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 15 (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 (35 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.** <!-- 35 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. -->
+16 -9
View File
@@ -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