analysis: add 16 evidence-backed drift findings
Twelve drift findings and four works-well findings, each citing file paths, line numbers, commit hashes or rows in analysis/data/. Every 'neckbeard mechanism' field names a concrete rule in neckbeard v0.1.1 or states explicitly that none exists.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# F-001: Milestone M5 was decided and executed, but the steering docs still call it an open question
|
||||
|
||||
category: claim-vs-reality
|
||||
confidence: high
|
||||
|
||||
evidence:
|
||||
- `roadmap.md:6-14` (last commit 2026-08-09, `git log -1 main -- roadmap.md`) states
|
||||
the milestone set as "M1–M4", gives the distribution "M1 33 · M2 21 · M3 4 · M4 12
|
||||
von 70" under "Stand 2026-08-06", and frames the Härtung milestone as an explicitly
|
||||
open question: "Ob das ein eigener Meilenstein „Härtung" werden soll oder M1 bewusst
|
||||
breit bleibt, ist **offen** — zu entscheiden im Refinement, nicht nebenbei."
|
||||
- `CLAUDE.md:98` (last commit 2026-08-09) likewise binds every issue to "Gruppen-Milestones
|
||||
M1–M4".
|
||||
- Measured against the live tracker (`analysis/data/gitlab_issues.json`, 71 open issues):
|
||||
M1 19 · M2 22 · M3 4 · M4 12 · **M5 — Härtung 14**. The milestone exists and is populated.
|
||||
- All 14 M5 issues live in `axion1337.chat-gitops` and carry `updated_at` 2026-08-09.
|
||||
- `verfahren/retro/2026-08-09.md:115-117` records the decision: "**M5 — Härtung** angelegt,
|
||||
14 Issues aus M1 verschoben", with the dividing line and the resulting distribution
|
||||
"M1 18 · M2 21 · M3 4 · M4 13 · M5 14".
|
||||
- No ADR covers it: `decisions/` holds 0001-0009, none mentions M5 or Härtung
|
||||
(`grep -rn 'M5\|Härtung' decisions/` -> no match).
|
||||
- The retro's own numbers are already stale on the day they were written: retro says
|
||||
M1 18 / M2 21 / M4 13 / 70 total, the tracker says M1 19 / M2 22 / M4 12 / 71 total.
|
||||
|
||||
impact: The two documents a new session is told to read first (`CLAUDE.md`, then
|
||||
`roadmap.md`) describe a milestone set that no longer exists, and pose as undecided a
|
||||
question that was decided. An agent following `CLAUDE.md:98` would file a hardening issue
|
||||
into M1, silently undoing the split. The decision itself survives only as a bullet in a
|
||||
dated retro file — the one place the repo's own rules do not point sessions to.
|
||||
`CLAUDE.md:79-82` makes an ADR mandatory for process decisions and calls documenting an
|
||||
exception instead of deciding it "ein Fehler"; by the repo's own standard this decision is
|
||||
under-recorded.
|
||||
|
||||
root-cause pattern: **Decision executed in the tool, documentation not carried along.**
|
||||
The change was made where the work is (GitLab milestones), noted where the session ended
|
||||
(the retro), and never propagated to the documents that claim to be canonical.
|
||||
|
||||
neckbeard mechanism: Partly prevented, partly a gap.
|
||||
Prevented: ADR-0002 (`docs/adr/0002-in-repo-issues.md`, Decision) puts issues in the repo
|
||||
as `docs/issues/NNNN-slug.md`, so re-bucketing 14 issues *is* a commit in the same tree as
|
||||
`roadmap.md` — a reviewer sees the docs that were not updated alongside it. `AGENTS.md:79`
|
||||
plus `scripts/gen_status.py` make `STATUS.md` a generated index ("do not edit by hand"), so
|
||||
counts are never hand-copied prose that can rot. `WORKFLOW.md:128` puts "STATUS review:
|
||||
anything stale or surprising" on the refinement agenda.
|
||||
**Gap in neckbeard:** it has no milestone or roadmap concept at all — no artifact type in
|
||||
`schema.yaml` groups issues into a delivery bucket, and nothing would have flagged the
|
||||
M1-vs-M5 split as needing an ADR. Session 2 must decide where "worauf zahlt es ein" lives.
|
||||
@@ -0,0 +1,56 @@
|
||||
# F-002: The timestamp anonymisation rule reaches one of six repos; 237 commits still publish real working hours
|
||||
|
||||
category: claim-vs-reality
|
||||
confidence: high
|
||||
|
||||
evidence:
|
||||
- `CLAUDE.md:116-134` declares the rule for "**alle** Repos der Gruppe `axion1337.chat`
|
||||
und die ThreadNet-Dienste": author *and* committer date set to 12:00:00 UTC, with the
|
||||
stated purpose "damit sich aus der Historie keine persönlichen Arbeitszeiten ablesen
|
||||
lassen".
|
||||
- Measured over every ref of all six repos (`analysis/data/timestamp_anonymisation.tsv`,
|
||||
produced by `analysis/scripts/inv_timestamps.py`, author time rendered in UTC):
|
||||
|
||||
| repo | own/agent commits with real clock time |
|
||||
|---|---|
|
||||
| management | 0 |
|
||||
| axion1337.chat-gitops | 187 |
|
||||
| threadnet-operating | 21 |
|
||||
| thread-net-git | 11 |
|
||||
| threadnet-call | 14 (all on `origin/livekit`) |
|
||||
| ThreadNet-Web | 4 |
|
||||
|
||||
Total 237. Upstream Element/Matrix commits are excluded from that count — they were
|
||||
never this project's to rewrite.
|
||||
- `thread-net-git` and `threadnet-operating` were not rewritten at all: they hold **no**
|
||||
12:00:00 commits and appear in no section of `shared/commit-zuordnung-2026-08-07.md`
|
||||
(sections cover gitops, management, ThreadNet-Web, threadnet-call only).
|
||||
- The exposed times are exactly the ones the rule exists to hide. On
|
||||
`axion1337.chat-gitops`'s `turn-secret-rotation-20260728-192656` branch alone, the
|
||||
hour-of-day distribution of the 41 commits is 00h ×1, 01h ×6, 21h ×8, 22h ×1, 23h ×6.
|
||||
- All of this is publicly readable: every one of the six repos mirrors to
|
||||
`rohana.axion1337.de`, verified in sync by `git ls-remote` (see F-013).
|
||||
- `CLAUDE.md:143-147` does carry a caveat, but it names push times, issue and comment
|
||||
timestamps, pipeline runs and package publications — **not** the git history itself.
|
||||
`shared/commit-zuordnung-2026-08-07.md:41-44` discloses the untouched pre-boundary
|
||||
commits, but frames it as an SHA matter ("haben ihre SHA behalten"), not as a
|
||||
147-commit hole in the privacy goal.
|
||||
|
||||
impact: The rule's stated goal is not achieved for five of six repos, while both the rule
|
||||
and its caveat read as though the remaining exposure were limited to non-git surfaces.
|
||||
Anyone reading `CLAUDE.md` would reasonably conclude the git history is clean. Effort was
|
||||
spent (a 251-commit rewrite, three restarted release pipelines, a mapping document) for a
|
||||
result that a `git log` on any side branch undoes.
|
||||
|
||||
root-cause pattern: **Rule declared repo-wide, applied to a subset — and the gap between
|
||||
the two is documented in a third place, in different terms.**
|
||||
|
||||
neckbeard mechanism: **Gap in neckbeard.** Nothing in `AGENTS.md`, `WORKFLOW.md`,
|
||||
`schema.yaml` or `scripts/validate.py` concerns commit hygiene, author identity or history
|
||||
rewriting; `docs/aar/2026-08-09-neckbeard-v1-creation.md` ("Git is the changelog") treats
|
||||
git as a given, not as an artifact under governance. The nearest applicable rule is
|
||||
`AGENTS.md:50-55` ("Never claim something works without evidence... Uncertainty is
|
||||
reported, never swallowed"), which is a behavioural rule for an agent, not a check on the
|
||||
repo. A field test finding worth carrying back: a framework that governs documents while
|
||||
the project's most sensitive claims are about its *git history* leaves that surface
|
||||
unguarded.
|
||||
@@ -0,0 +1,34 @@
|
||||
# F-003: Five author identities of one person remain, one with a malformed e-mail
|
||||
|
||||
category: claim-vs-reality
|
||||
confidence: high
|
||||
|
||||
evidence:
|
||||
- `shared/commit-zuordnung-2026-08-07.md:19-22` states that the rewrite unified author
|
||||
identity: "bei einigen Commits zusätzlich die Autoren-Identität (drei Varianten
|
||||
derselben Person auf eine vereinheitlicht)".
|
||||
- Author fields still present across the six repos
|
||||
(`analysis/data/timestamp_anonymisation.tsv`, column `author`):
|
||||
`Thore Cimbal <cfx@riot.8shield.net>`, `Scrublord MacBad <Scrublord@Mac.Bad>`,
|
||||
`ScrublordMcBad <gamemaster@axion1337.de>`, `sorb <gamemaster@axion1337.de>`,
|
||||
`sorB <cfxqriot.8shield.net>`.
|
||||
- That is five variants, not the three the document says were collapsed into one.
|
||||
- `sorB <cfxqriot.8shield.net>` (2 commits on `ThreadNet-Web` main) is not a valid e-mail
|
||||
address — the `@` is missing, `cfx@riot` became `cfxqriot`.
|
||||
- The unification reached only the rewritten commits: `management` main shows a single
|
||||
identity, while `axion1337.chat-gitops` main still carries 135 commits as
|
||||
`Scrublord MacBad <Scrublord@Mac.Bad>` and 10 as `ScrublordMcBad`.
|
||||
|
||||
impact: Same exposure class as F-002 and the same false sense of completion. In addition,
|
||||
the malformed address makes those two commits unattributable by tooling that matches on
|
||||
e-mail, and `Scrublord@Mac.Bad` leaks a machine name. Contributor statistics on the public
|
||||
Gitea mirror show one person as four or five contributors.
|
||||
|
||||
root-cause pattern: **Rule declared repo-wide, applied to a subset** — the same pattern as
|
||||
F-002, in the same operation. Both were verified for the property that was easy to check
|
||||
(SHA pairs matched) and not for the property the operation existed to achieve.
|
||||
|
||||
neckbeard mechanism: **Gap in neckbeard**, as in F-002 — no commit or identity hygiene
|
||||
exists in the framework. The closest structural analogue is `scripts/validate.py`, which
|
||||
enforces that declared metadata matches reality for *documents* (`check_fields`,
|
||||
`apply_rules`); nothing does the equivalent for commit metadata.
|
||||
@@ -0,0 +1,46 @@
|
||||
# F-004: Open work items live only in host markdown, against the repo's own "no backlog markdown" rule
|
||||
|
||||
category: process-drift
|
||||
confidence: high
|
||||
|
||||
evidence:
|
||||
- The rule, twice stated: `CLAUDE.md:72-75` — "**Alles Offene ist ein Issue** … Kein neues
|
||||
Backlog-Markdown anlegen; `hosts/`/`shared/` sind nur Bestand + Historie" — and
|
||||
`README.md:47` — "Alle offenen Punkte sind **Issues in diesem Projekt**".
|
||||
- Open next steps that exist only as prose in `hosts/` and `shared/`:
|
||||
- `hosts/overmind.md:90` — "**Nächster Schritt:** `element-desktop-build` von rohana in
|
||||
die Lab-Registry umziehen … bewusst zurückgestellt" (section OVERMIND-01, marked
|
||||
"Status: erledigt").
|
||||
- `hosts/overmind.md:111-115` — "**Offen:** … Falls der Hang trotz EEE-off + neuer
|
||||
Firmware wiederkehrt: gezielter ASPM-Fix statt globalem Kernel-Parameter".
|
||||
- `hosts/cfgmon.md:148` — "**Nächster Schritt:** die drei manuellen Schritte oben, dann
|
||||
→ erledigt" (section CFGMON-11).
|
||||
- `shared/branding.md:210` — a whole `## Offen` section.
|
||||
- Section IDs in `hosts/`/`shared/` with **no** corresponding issue anywhere in the group
|
||||
(checked against all 111 issues in `analysis/data/gitlab_issues.json`):
|
||||
`OVERMIND-01`, `CFGMON-11`, `CFGMON-12`, `CFGMON-13`, `MATRIX-05`.
|
||||
For contrast, `OVERMIND-02` -> management#4 and `LABNET-01` -> management#11 do exist.
|
||||
- `hosts/overmind.md:146` even routes readers to a markdown-only item as if it were
|
||||
tracked: "CFGMON-11 (Gitea-CI-Rückbau)".
|
||||
|
||||
impact: An item like the `element-desktop-build` migration is invisible to the group board,
|
||||
carries no milestone, no priority and no owner, and appears in no refinement. It is
|
||||
deferred with a reason — a good reason — inside a file the process declares to be history.
|
||||
`README.md:74` names the distinction between "gemacht" and "bewusst gelassen" as the most
|
||||
frequent question; for these five IDs the answer exists in exactly one place and it is not
|
||||
the one the process points at.
|
||||
|
||||
root-cause pattern: **Two backlogs, one rule.** The rule says one system; the host
|
||||
documents are the natural place to write while working on a host, so items land there and
|
||||
the migration to an issue is a separate act of discipline that sometimes does not happen.
|
||||
|
||||
neckbeard mechanism: Directly prevented by ADR-0002
|
||||
(`docs/adr/0002-in-repo-issues.md`, Decision: "In-repo Markdown issues
|
||||
(`docs/issues/NNNN-slug.md`, status in frontmatter, generated `STATUS.md` as index)").
|
||||
Under it there is no gap between "writing in the doc" and "filing an issue" — both are
|
||||
markdown files in the same tree, and `scripts/validate.py` enforces that an issue has a
|
||||
`status` from `schema.yaml`'s enum `[open, in-progress, done, rejected]` (`check_fields`,
|
||||
`schema.yaml:76-84`), so an open item cannot exist as a bold prose label. `scripts/gen_status.py`
|
||||
then lists it in `STATUS.md` whether or not anyone remembered it.
|
||||
Residual gap: nothing in neckbeard would stop a wiki page under `docs/wiki/` from carrying
|
||||
an "Offen" section; the guard is `WORKFLOW.md:126` wiki lint, a human ritual.
|
||||
@@ -0,0 +1,44 @@
|
||||
# F-005: A live document routes the reader to the Gitea tracker that three documents declare dead
|
||||
|
||||
category: claim-vs-reality
|
||||
confidence: high
|
||||
|
||||
evidence:
|
||||
- The claim, stated three times and each time absolutely:
|
||||
- `README.md:22-27` — "**Keine Ausnahmen mehr.** … der Gitea-Tracker ist leer …
|
||||
**Alle Issues leben auf git.lab.**"
|
||||
- `hosts/overmind.md:38-42` — "**Issues nicht mehr** … der Tracker ist leer.
|
||||
**Ohne Ausnahme: Issues leben auf git.lab.**"
|
||||
- `CLAUDE.md:49` — "**Issues leben auf git.lab.** Die alten Gitea-Issues sind
|
||||
geschlossen und verweisen dorthin."
|
||||
- `hosts/cfgmon.md:176` (section CFGMON-12) resolves its status against that tracker:
|
||||
"**Status:** abgelöst durch [gitops#48](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/48)
|
||||
(2026-08-01, sorb: HOHE Priorität …) — Plan-Skizze und die offene
|
||||
Erreichbarkeits-Entscheidung git.lab-only vs. extern stehen dort".
|
||||
- So the substance of an open decision is said to live in an issue on the tracker that is
|
||||
declared empty. The link is the only pointer to it in the repo.
|
||||
- The number is additionally unsafe: `CLAUDE.md:50-52` warns that gitops issue numbers
|
||||
shifted in the migration ("z. B. Gitea#48 → GitLab#46") and that "alte „gitops#N"-Verweise
|
||||
meinen die Gitea-Nummer". A reader who resolves `gitops#48` on git.lab lands on a
|
||||
different issue than the one meant — and `analysis/data/links.tsv` records
|
||||
`axion1337.chat-gitops#48` as an existing GitLab issue, so nothing looks broken.
|
||||
- CFGMON-12 has no issue of its own anywhere in the group (F-004).
|
||||
|
||||
impact: A high-priority decision ("HOHE Priorität", plus an open reachability question) is
|
||||
reachable only through a link into a system the documentation says is retired, under a
|
||||
number the documentation says is ambiguous. Both failure modes are silent: the link
|
||||
resolves, and so does the wrong git.lab issue.
|
||||
|
||||
root-cause pattern: **Decision executed in the tool, documentation not carried along** —
|
||||
the migration was completed and announced, but the documents that pointed into the old
|
||||
system were not swept. Compounded by **two backlogs, one rule** (F-004).
|
||||
|
||||
neckbeard mechanism: Removed by construction under ADR-0002
|
||||
(`docs/adr/0002-in-repo-issues.md`): with issues as files in the repo, a reference to an
|
||||
issue is a relative link, and `scripts/validate.py`'s `check_body_links` reports
|
||||
"inline link target missing" when it does not resolve — a renumbering or a retired tracker
|
||||
becomes a failing check instead of a working link to the wrong thing. ADR-0002 explicitly
|
||||
lists this as the con of forge issues: "issue ↔ artifact links leave the repo".
|
||||
**Gap in neckbeard:** `check_body_links` only validates repo-relative targets; external
|
||||
URLs are format-checked at most (`schema.yaml:22-26` applies that only to frontmatter link
|
||||
fields). A doc pointing at a dead external tracker stays green.
|
||||
@@ -0,0 +1,43 @@
|
||||
# F-006: Three abandoned branches keep publishing exactly what the rewrite removed from main
|
||||
|
||||
category: dead-artifact
|
||||
confidence: high
|
||||
|
||||
evidence:
|
||||
- `axion1337.chat-gitops` `origin/turn-secret-rotation-20260728-192656`: 41 commits not on
|
||||
main, head `f30d2f5` dated 2026-07-28, **not merged** (`git merge-base --is-ancestor` ->
|
||||
no). All 41 carry real clock times (00h–01h and 21h–23h heavy). They are the *pre-rewrite*
|
||||
versions of commits the mapping table lists as replaced — 42 mapping rows resolve as
|
||||
`ok-both-present` in `analysis/data/sha_refs.tsv`, meaning the old SHA is still reachable.
|
||||
- The same branch exists on the public mirror:
|
||||
`git ls-remote https://rohana.axion1337.de/sorb/axion1337.chat-gitops.git` ->
|
||||
`f30d2f582ba14d85a7dec97c4db9bb2677ca824d refs/heads/turn-secret-rotation-20260728-192656`,
|
||||
identical to git.lab.
|
||||
- `threadnet-call` `origin/livekit`: 14 own commits with real clock times, head `c12a4116`
|
||||
(2026-07-31 22:22), mirrored publicly with the same head.
|
||||
- `axion1337.chat-gitops` `origin/wiki`: head `0ff598e`, 2026-05-14. `CLAUDE.md:63` already
|
||||
describes it as "ein überholter Mai-Abzug von `docs/`", and management#19 ("DOC-02:
|
||||
Veralteten `wiki`-Branch im gitops-Repo entfernen?") tracks it — open, `priority:low`,
|
||||
no `status:` label, i.e. plain backlog.
|
||||
- The rotation branch's own successor mechanism is live: the scheduled CI job
|
||||
`canonize_rotation` exists at `.gitlab-ci.yml:51` in the gitops repo, as `CLAUDE.md:56-60`
|
||||
describes. The branch predates it and was never cleaned up.
|
||||
|
||||
impact: The single most expensive operation in this repo's history — a 251-commit rewrite
|
||||
that restarted three release pipelines and needed its own mapping document — is undone for
|
||||
42 of those commits by a branch nobody looks at, on a host that is public. The `wiki` branch
|
||||
additionally offers a three-month-old copy of the documentation next to the current one,
|
||||
which is the "zwei Wahrheiten über dieselbe Sache" failure `CLAUDE.md:94-97` names as worse
|
||||
than an incomplete one.
|
||||
|
||||
root-cause pattern: **Artifacts outlive their purpose without an owner.** All three branches
|
||||
served a finished purpose; none had a defined end. The one that is noticed (`wiki`) sits in
|
||||
the backlog without a status label, which is where low-priority hygiene goes to rest.
|
||||
|
||||
neckbeard mechanism: **Gap in neckbeard.** The framework governs files inside a checkout,
|
||||
not refs: nothing in `AGENTS.md`, `WORKFLOW.md` or `scripts/validate.py` sees a branch.
|
||||
The nearest existing mechanisms are `WORKFLOW.md:122` (refinement item 2, "Backlog triage
|
||||
over `docs/issues/`") and `WORKFLOW.md:126` (wiki lint, which hunts "orphan pages") — both
|
||||
would need a branch equivalent to catch this. `schema.yaml`'s `warn_if_orphan` rule shows
|
||||
the framework already accepts "reachable from nowhere" as a reportable condition for
|
||||
documents; the field test shows refs need the same treatment.
|
||||
@@ -0,0 +1,47 @@
|
||||
# F-007: How many repos are mirrored has three different answers, and one of them is wrong
|
||||
|
||||
category: claim-vs-reality
|
||||
confidence: high
|
||||
|
||||
evidence:
|
||||
- `CLAUDE.md:36-37` — "**Gespiegelt wird nur die Gruppe `axion1337.chat`** (die **fünf**
|
||||
Produkt-Repos und `management`)" — i.e. six repos.
|
||||
- `hosts/overmind.md:27-31` — "Stand 2026-08-09 **sieben**: die **sechs** Produkt-Repos
|
||||
(ThreadNet-Web, threadnet-call, thread-net-git, threadnet-operating,
|
||||
axion1337.chat-gitops, seit heute auch `game-operating`) **und `management`**".
|
||||
- Both files were last committed on the same day, 2026-08-09
|
||||
(`git log -1 main -- <file>`), so this is not a lag between an old and a new statement;
|
||||
the repo asserts five and six product repos simultaneously.
|
||||
- Measured (`git ls-remote` against both hosts, heads of `main`):
|
||||
- management, axion1337.chat-gitops, ThreadNet-Web, threadnet-call, thread-net-git,
|
||||
threadnet-operating -> **all six in sync**, identical SHAs.
|
||||
- `game-operating` -> git.lab `ead1272823`, `rohana.axion1337.de/sorb/game-operating`
|
||||
-> **no repository**. The "seit heute auch `game-operating`" claim does not hold.
|
||||
- `gameserver` -> git.lab `d5c6ccb2e9`, Gitea `48441a500f`, **diverged** — which is
|
||||
exactly what `hosts/overmind.md:33-35` says and management#32 tracks.
|
||||
- Caveat on the negative result: the mirror naming convention is `sorb/<name>` for all
|
||||
six verified repos; a `game-operating` mirror under a different name would not have
|
||||
been found. Confidence high on the contradiction, medium on the specific cause.
|
||||
|
||||
impact: The mirror set is the answer to "what can the Hetzner cluster still reach when the
|
||||
lab is down" — the reason the two-host topology exists at all (`CLAUDE.md:26-33`). A repo
|
||||
believed mirrored but not mirrored is a gap in exactly that guarantee, and the belief is
|
||||
recorded in the file that is meant to be authoritative for host questions.
|
||||
management#28 ("MIRROR-01: Ein Ausfall der Push-Mirrors bleibt unbemerkt") shows the risk
|
||||
class is known; it sits in the backlog at `priority:low` with no status label.
|
||||
|
||||
root-cause pattern: **Decision executed in the tool, documentation not carried along** —
|
||||
here in the inverse direction: the document was updated ahead of the tool, and the number
|
||||
in the *other* document was not touched at all.
|
||||
|
||||
neckbeard mechanism: `WORKFLOW.md:136-138` (Knowledge Handling) is the governing rule:
|
||||
"Contradictions are resolved or explicitly flagged — never left silently coexisting", with
|
||||
the detection ritual at `WORKFLOW.md:126` (refinement item 4: "Wiki lint (content-level,
|
||||
beyond `validate.py`): contradictions between pages, claims superseded by newer sources").
|
||||
`AGENTS.md:50-52` adds "Never claim something works without evidence".
|
||||
**Partial gap:** the rule exists, the detection is a human ritual on a weekly cadence, and
|
||||
`scripts/validate.py` checks structure and link targets only — two numbers contradicting
|
||||
each other across two files is invisible to it. `AGENTS.md:101-103` ("If a deterministic job
|
||||
lacks a script, propose one") points the right way, but "is this claim still true" is not
|
||||
deterministic in general. The tractable subset — a claim of the form "N repos are mirrored"
|
||||
— is checkable, and this field test wrote such a check ad hoc.
|
||||
@@ -0,0 +1,42 @@
|
||||
# F-008: The five components have four naming schemes, and one display name maps to a repo that shares no name with it
|
||||
|
||||
category: structural-drift
|
||||
confidence: high
|
||||
|
||||
evidence:
|
||||
- Slugs verified against git.lab with `git ls-remote` (see `analysis/SCOPE.md` §3):
|
||||
`threadnet-call`, `thread-net-git`, `threadnet-operating`, `ThreadNet-Web`,
|
||||
`axion1337.chat-gitops`.
|
||||
- `thread-net-git` breaks the `threadnet-*` pattern of its siblings;
|
||||
`https://git.lab/axion1337.chat/threadnet-git.git` returns "project could not be found".
|
||||
- `ThreadNet-Web` is the only CamelCase slug. `ThreadNet-Web` and `threadnet-web` return
|
||||
byte-identical ref lists (same project via GitLab's case-insensitive path handling), and
|
||||
the repo's own documents use both plus a lowercase registry path
|
||||
`registry.git.lab/axion1337.chat/threadnet-web/desktop-build` (`hosts/overmind.md:54`).
|
||||
`hosts/matrix.md:20` uses the Gitea spelling `sorb/ThreadNet-Web`.
|
||||
- "ThreadNet Server Suite" = `axion1337.chat-gitops` rests on a single sentence,
|
||||
`CLAUDE.md:6`. The slug contains no ThreadNet element; nothing else in the repo repeats
|
||||
the equation. This session had to ask the human to confirm it (Phase-0 STOP, question 3).
|
||||
- `vision/threadnet.md:9-11` lists the product line as "ThreadNet-Web …, threadnet-call
|
||||
…, thread-net-git, threadnet-operating" — four names in three casings, and the fifth
|
||||
component is absent from the vision that defines the line.
|
||||
|
||||
impact: Nothing in the repo lets a reader or an agent get from a display name to a
|
||||
repository deterministically. This session could not, and the Phase-0 scope freeze existed
|
||||
partly to resolve it. Every automation that iterates over "the ThreadNet repos" needs a
|
||||
hand-maintained table, and the case-insensitive redirect hides the inconsistency until
|
||||
something case-sensitive (a container path, a CI cache key, a `git clone` into a
|
||||
case-sensitive filesystem) meets it.
|
||||
|
||||
root-cause pattern: **Rule declared repo-wide, applied to a subset** — the branding
|
||||
decision in `shared/branding.md` fixes what the *product* is called on every surface, and
|
||||
was carried through thoroughly there, but repository names were never part of that sweep.
|
||||
|
||||
neckbeard mechanism: **Gap in neckbeard**, with a near-miss worth noting. `schema.yaml`
|
||||
enforces naming as a first-class rule for artifacts — `filename: "^\\d{4}-[a-z0-9-]+\\.md$"`
|
||||
for ADRs and issues, `"^[a-z0-9-]+\\.md$"` for wiki pages — and `scripts/validate.py` reports
|
||||
"filename does not match" plus "id does not match filename prefix". The framework therefore
|
||||
already holds the position that names must be mechanically derivable. It applies that only
|
||||
inside one repo; a multi-repo project has no equivalent, and neckbeard has no artifact type
|
||||
that declares "these are the components and this is what they are called". Session 2 needs
|
||||
one; today `analysis/SCOPE.md` §3 is the only such table that exists.
|
||||
@@ -0,0 +1,47 @@
|
||||
# F-009: Documentation attention and actual work mass point at different components
|
||||
|
||||
category: structural-drift
|
||||
confidence: medium
|
||||
|
||||
evidence:
|
||||
- Open issues per component (`analysis/data/gitlab_issues.json`):
|
||||
`axion1337.chat-gitops` 35 · `management` 26 · `ThreadNet-Web` 9 · `threadnet-call` 1 ·
|
||||
`thread-net-git` 0 · `threadnet-operating` 0.
|
||||
- Commit volume over all refs (`analysis/data/git_activity.tsv`):
|
||||
`axion1337.chat-gitops` 269 · `ThreadNet-Web` 52 · `threadnet-operating` 21 ·
|
||||
`threadnet-call` 12 own · `thread-net-git` 11.
|
||||
- Repository substance (`analysis/data/tree_*.txt`): `thread-net-git` holds **4 tracked
|
||||
files** (`.gitignore`, `README.md`, `docker-compose.yml`, one file under `backup/`) and
|
||||
one markdown document; last commit 2026-08-02, 7 days before the reference date.
|
||||
`threadnet-operating` holds 27 files, 2 documents.
|
||||
- Against that, `vision/threadnet.md:9-11` presents all four ThreadNet repos as equal
|
||||
members of the product line, and `roadmap.md:50-62` gives the ThreadNet line six numbered
|
||||
items, five of which concern `ThreadNet-Web` and none of which concern `thread-net-git`
|
||||
or `threadnet-operating`.
|
||||
- The component carrying half the open work, `axion1337.chat-gitops`, is named in the
|
||||
vision document **not at all** and in `CLAUDE.md` once, parenthetically, as an equation
|
||||
to a display name (`CLAUDE.md:6`).
|
||||
- All 14 issues of the newly created M5 — Härtung milestone are in `axion1337.chat-gitops`.
|
||||
|
||||
impact: A reader forming a mental model from `vision/` and `roadmap.md` will weight five
|
||||
components roughly equally and will not learn that one of them holds half the backlog while
|
||||
two are effectively dormant. Session-start reading therefore mis-primes exactly the
|
||||
prioritisation that the roadmap exists to steer. This is a mismatch, not a defect: dormancy
|
||||
may be entirely intended — but the documents do not say so, and `README.md:68-70` requires
|
||||
each point to describe "den tatsächlichen Zustand".
|
||||
|
||||
root-cause pattern: **Documents describe the intended shape, the tracker holds the actual
|
||||
one, and nothing reconciles them.** Related to F-001 in mechanism: the tracker is where
|
||||
reality accumulates, the documents are written once.
|
||||
|
||||
neckbeard mechanism: `AGENTS.md:79` plus `scripts/gen_status.py`: `STATUS.md` is a generated
|
||||
overview of open issues, active designs and recent ADRs, explicitly "do not edit by hand",
|
||||
and `WORKFLOW.md:128` puts reviewing it for "anything stale or surprising" on the refinement
|
||||
agenda. With ADR-0002's in-repo issues, work mass is countable from the tree, so the
|
||||
imbalance would be visible in a generated artifact rather than inferable only from an API.
|
||||
**Gap in neckbeard:** `gen_status.py` covers one repo. This project is six, and neckbeard
|
||||
has no cross-repo status concept — the same missing "components" artifact as in F-008.
|
||||
|
||||
confidence note: medium rather than high — the underlying counts are exact, but "attention
|
||||
should follow work mass" is a judgement about intent, and dormant-by-design is a legitimate
|
||||
answer the documents simply do not give.
|
||||
@@ -0,0 +1,53 @@
|
||||
# F-010: Hand-written "Stand" labels are older than the commits that touched the same file
|
||||
|
||||
category: doc-drift
|
||||
confidence: medium
|
||||
|
||||
evidence:
|
||||
- Label vs. last commit on `main` (`analysis/data/docs_inventory.tsv`, `git log -1`):
|
||||
|
||||
| file | "Stand" label | last commit |
|
||||
|---|---|---|
|
||||
| `CLAUDE.md:18` | 2026-08-01 | 2026-08-09 |
|
||||
| `roadmap.md:3` | 2026-08-06 | 2026-08-09 |
|
||||
| `hosts/overmind.md:14` | 2026-07-31 | 2026-08-09 |
|
||||
|
||||
- The labels are not merely old, they are contradicted inside their own file:
|
||||
`hosts/overmind.md` carries the table label "**Stand** 2026-07-31" at line 14 and the
|
||||
sentence "Stand 2026-08-09 **sieben**" at line 28.
|
||||
- `CLAUDE.md` is labelled "Projektrealitäten (Stand 2026-08-01)" while containing a rule
|
||||
section headed "Commit-Konventionen (seit 2026-08-07)" — six days newer than the label
|
||||
above it.
|
||||
- Scale of the surface: 40 documents, 813 extracted claim lines, of which 68 carry an
|
||||
explicit date assertion (`analysis/data/claims.tsv`, trigger `date-claim`).
|
||||
- The whole document set is young — every management doc is ≤ 8 days old
|
||||
(`docs_inventory.tsv`, max `days_since_change` = 8) — so this is drift accumulating at
|
||||
high speed, not neglect over time.
|
||||
|
||||
impact: The label is what a reader uses to decide whether to trust a section, and here it
|
||||
systematically under-reports freshness, which trains readers to distrust current content
|
||||
and, worse, gives no signal at all when a section really is stale. In `roadmap.md` the
|
||||
combination is actively misleading: "Stand 2026-08-06" over numbers that were already wrong
|
||||
on 2026-08-09 (F-001).
|
||||
|
||||
root-cause pattern: **Hand-maintained metadata about a file, kept inside the file.**
|
||||
Git already knows the answer; the label is a second source of truth that must be updated by
|
||||
hand and therefore is not.
|
||||
|
||||
neckbeard mechanism: Prevented by `AGENTS.md:79` — `STATUS.md` is "Generated overview …
|
||||
do not edit by hand", produced by `scripts/gen_status.py` — combined with
|
||||
`AGENTS.md:101-103`: "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." A file's freshness is exactly such a deterministic job.
|
||||
`docs/aar/2026-08-09-neckbeard-v1-creation.md` reinforces it from experience:
|
||||
"Deterministic jobs belong in scripts, not inference — this rule paid for itself repeatedly",
|
||||
and "Git is the changelog. No separate log file." (`WORKFLOW.md:140`) is the same principle
|
||||
applied to history.
|
||||
Note for Session 2: `schema.yaml` requires a `date` field on adr, design and aar artifacts
|
||||
(`kind: date`), which is a *creation/decision* date and legitimately hand-set — that is not
|
||||
the same thing as a freshness label and should not be conflated when migrating.
|
||||
|
||||
confidence note: medium — the dates are exact, but a "Stand" label may be intended as
|
||||
"state of the described system as last verified", not "state of this text". Under that
|
||||
reading the labels are defensible for `hosts/overmind.md`; they are not for
|
||||
`roadmap.md`, whose own header says "Zahlen hier veralten, das Board nicht".
|
||||
@@ -0,0 +1,44 @@
|
||||
# F-011: "Project repos have their own CLAUDE.mds" holds for one of five components
|
||||
|
||||
category: claim-vs-reality
|
||||
confidence: medium
|
||||
|
||||
evidence:
|
||||
- `CLAUDE.md:5-7` — "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."
|
||||
- Present in the five component checkouts (`analysis/data/tree_*.txt`):
|
||||
`axion1337.chat-gitops/CLAUDE.md` — yes. `threadnet-call`, `thread-net-git`,
|
||||
`threadnet-operating`, `ThreadNet-Web` — none.
|
||||
- The conflict-resolution rule the sentence sets up ("bei Widerspruch gilt … **diese**
|
||||
Datei") therefore has nothing to resolve in four of five repos, and a session opened
|
||||
directly in one of those four gets no pointer to the canonical conventions at all.
|
||||
- The one that exists is consistent with its claim: `CLAUDE.md:230-233` says the Karpathy
|
||||
block was taken verbatim from the gitops CLAUDE.md and must not be edited. Verified —
|
||||
the block is byte-identical in both files; the management copy only appends its own
|
||||
closing note after it. That claim holds.
|
||||
|
||||
impact: Four of five component repos have no session-start instructions. The conventions
|
||||
that `CLAUDE.md:118` declares binding for "**alle** Repos der Gruppe" (commit style,
|
||||
timestamp anonymisation, secrets handling) are unreachable from inside those repos — which
|
||||
is a plausible contributing cause of F-002 and F-003, where exactly those four repos are
|
||||
the ones the rules did not reach.
|
||||
|
||||
root-cause pattern: **Rule declared repo-wide, applied to a subset** — and here the
|
||||
distribution mechanism for the rules is itself the thing that was only partly applied.
|
||||
|
||||
neckbeard mechanism: This is the exact problem ADR-0001
|
||||
(`docs/adr/0001-agents-md-canonical.md`) decides. Its Context names it outright:
|
||||
"Maintaining per-harness copies of the rules would guarantee drift", and its Decision makes
|
||||
`AGENTS.md` the single rule file with `CLAUDE.md` reduced to a one-line pointer — the
|
||||
neckbeard repo's own `CLAUDE.md` is exactly that one line ("Read AGENTS.md — the canonical
|
||||
instruction file for this repository. All rules live there."). Adopting it converts the
|
||||
present claim into a checkable fact: each repo carries a pointer, the rules exist once.
|
||||
**Gap in neckbeard:** ADR-0001 solves one-repo-many-harnesses; it does not address
|
||||
many-repos-one-ruleset. Nothing in the framework says how five component repos share the
|
||||
governing `AGENTS.md` — vendoring a copy re-creates the drift ADR-0001 rejects, and a link
|
||||
to another repo breaks the portability ADR-0003 protects. Session 2 has to answer this, and
|
||||
it is the sharpest framework question this field test produced.
|
||||
|
||||
confidence note: medium — the sentence can be read as descriptive-in-general rather than as
|
||||
a claim about all five repos, and it names only gitops as an example.
|
||||
@@ -0,0 +1,46 @@
|
||||
# F-012: Six commit references in the docs resolve nowhere, and nothing would have caught it
|
||||
|
||||
category: doc-drift
|
||||
confidence: high
|
||||
|
||||
evidence:
|
||||
- All SHA-shaped tokens in the 40 management documents were resolved against every ref of
|
||||
all six repos and against the rewrite mapping (`analysis/scripts/inv_shas.py` ->
|
||||
`analysis/data/sha_refs.tsv`): 22 resolve, **6 do not**.
|
||||
- The six, with their nature established individually:
|
||||
- `hosts/cfgmon.md:54` `dfe04c4a` and `verfahren/deploy-uebergabe.md:84` `dfe04c4` —
|
||||
a commit pushed directly to Gitea and overwritten by the mirror. Unresolvable **by
|
||||
design**; the surrounding text documents the loss ("vom Mirror überschrieben").
|
||||
- `verfahren/deploy-uebergabe.md:85` and
|
||||
`verfahren/aar/2026-08-01-cve-pipeline-gitops47.md:13` `2b715ca` — same class, the
|
||||
second documented overwrite ("2b715ca→0bd77e2").
|
||||
- `hosts/overmind.md:23` `7645a2b` — a commit in the `vendor/windows` repo, which the
|
||||
human placed out of scope; not resolvable here, not a defect.
|
||||
- `hosts/overmind.md:87` `5bc25447` — not a commit at all but a **container image tag**
|
||||
("Tags `5bc25447` + `stable`"); a false positive of the extractor, recorded as such.
|
||||
- So of six, four are intentional records of lost commits and two are out-of-scope or
|
||||
mis-typed as commits. **No accidental orphan was found** — and that is the finding worth
|
||||
keeping: the mapping list did its job (F-015).
|
||||
- What no mechanism provides: none of this was checkable before this session wrote a
|
||||
script for it. `analysis/data/links.tsv` covers markdown link targets and reports 2
|
||||
broken links in 11,390 (both malformed upstream Element changelog entries), but a SHA in
|
||||
prose is not a link and was never checked.
|
||||
|
||||
impact: Low today, structurally high. The repo cites commits as evidence throughout — it is
|
||||
the house style, and a good one (`README.md:68-70`, "woher stammt die Aussage?"). That style
|
||||
depends on citations staying resolvable across exactly the kind of history operation this
|
||||
project performed on 2026-08-07, and the only thing that kept it working was a document
|
||||
someone remembered to write by hand.
|
||||
|
||||
root-cause pattern: **Evidence-by-citation without a checker.** The convention is sound and
|
||||
followed; its integrity rests on human diligence at rewrite time.
|
||||
|
||||
neckbeard mechanism: **Gap in neckbeard.** `scripts/validate.py` checks link integrity in
|
||||
two places — `check_links` for frontmatter link fields and `check_body_links` for inline
|
||||
markdown links, erroring with "inline link target missing" — and `schema.yaml:19-20`
|
||||
extends body-link checking to root-level prose via `link_only: ["*.md"]`. Commit SHAs cited
|
||||
in prose are outside both. Given `AGENTS.md:101-103` ("Deterministic jobs … are done by
|
||||
scripts … If a deterministic job lacks a script, propose one"), resolving a cited SHA is a
|
||||
textbook deterministic job — `git cat-file -e` — and its absence is a real hole for any
|
||||
project whose docs cite commits. `analysis/scripts/inv_shas.py` is a working reference
|
||||
implementation.
|
||||
@@ -0,0 +1,49 @@
|
||||
# F-013: The mirror topology works exactly as documented — all six repos in sync
|
||||
|
||||
category: works-well
|
||||
confidence: high
|
||||
|
||||
evidence:
|
||||
- `main` heads compared between canonical git.lab and the Gitea mirror, both read live
|
||||
with `git ls-remote`:
|
||||
|
||||
| repo | git.lab | rohana/Gitea | |
|
||||
|---|---|---|---|
|
||||
| management | `2f012a6cd2` | `2f012a6cd2` | in sync |
|
||||
| axion1337.chat-gitops | `151ef3c8d0` | `151ef3c8d0` | in sync |
|
||||
| ThreadNet-Web | `d3bd5b20c2` | `d3bd5b20c2` | in sync |
|
||||
| threadnet-call | `d13cce7337` | `d13cce7337` | in sync |
|
||||
| thread-net-git | `2f4a15ce85` | `2f4a15ce85` | in sync |
|
||||
| threadnet-operating | `32f89afebf` | `32f89afebf` | in sync |
|
||||
|
||||
- The reasoning behind the topology is written down and is genuinely good:
|
||||
`CLAUDE.md:26-33` explains *why* two locations exist rather than only asserting it — the
|
||||
Hetzner cluster must rebuild when the lab is off — and pre-empts the tempting cleanup with
|
||||
a named warning: "⚠️ **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".
|
||||
- The failure mode is documented with its rescue procedure, not just prohibited:
|
||||
`README.md:16-20` and `CLAUDE.md:45-48` — pull the `.patch` from Gitea, `git am` to
|
||||
preserve authorship, push via git.lab.
|
||||
- The one standing exception is explicit, justified, bounded, and has a live mechanism:
|
||||
the TURN rotation CronJob (`CLAUDE.md:53-60`), canonised daily by the scheduled CI job
|
||||
`canonize_rotation`, present at `.gitlab-ci.yml:51` in the gitops repo. The alarm design
|
||||
is stated deliberately — "diese rote Pipeline **ist** der Alarm, einen zusätzlichen
|
||||
Termin gibt es bewusst nicht."
|
||||
|
||||
impact: This is the part of the current approach that must survive migration untouched.
|
||||
The value is not the sync itself but the form: a decision with its reason, its
|
||||
counter-argument, its rescue path, and its one exception each written where the next
|
||||
session will look.
|
||||
|
||||
root-cause pattern: **A decision recorded with its reasoning outlives the person who made
|
||||
it.** Every claim in this cluster held up under measurement; the ones that failed elsewhere
|
||||
in this report are the ones recorded as bare assertions.
|
||||
|
||||
neckbeard mechanism: This is what `docs/adr/` is for. `AGENTS.md:81` — "Architecture
|
||||
Decision Records — binding; never edited, only superseded" — and `AGENTS.md:89-90`, "Before
|
||||
proposing options (Gate 2), read the relevant ADRs and AARs first — past decisions and
|
||||
learnings are input, not trivia." The existing `decisions/0001-gitlab-kanonisch-push-mirror.md`
|
||||
already is an ADR in all but schema; migrating it needs frontmatter per `schema.yaml:39-52`
|
||||
(`type`, `id`, `status`, `date`) and nothing else. The "don't straighten this out" warning
|
||||
maps to the ADR Consequences section, and `WORKFLOW.md:56` ("Options & trade-offs … pro/contra
|
||||
each, chosen option, and why") is where the two-host reasoning belongs.
|
||||
@@ -0,0 +1,49 @@
|
||||
# F-014: The issue discipline the docs describe is actually lived — measurably
|
||||
|
||||
category: works-well
|
||||
confidence: high
|
||||
|
||||
evidence: all measured against `analysis/data/gitlab_issues.json` (111 issues, 71 open).
|
||||
- **Milestone duty** (`CLAUDE.md:98-103`, "Jedes Issue gehört zu genau einem Meilenstein"):
|
||||
**0 of 71** open issues lack a milestone. Across all four active projects, in every one.
|
||||
- **Exactly one status label** (`CLAUDE.md:76-78`, `README.md:60`): **0** issues carry more
|
||||
than one `status:` label.
|
||||
- **WIP limit 2** (`CLAUDE.md:77`, "**WIP-Limit 2** — auch sessionübergreifend zu
|
||||
verteidigen"): `status:doing` = **0**. The limit is not merely respected, there is no
|
||||
work parked as in-progress at all.
|
||||
- **No priority in titles** (`CLAUDE.md:90-97`, cleaned up 2026-08-06 after 34 migrated
|
||||
issues carried prefixes and two contradicted their own label): **0** open issues carry a
|
||||
`[HIGH]`/`[MEDIUM]`/`[LOW]` prefix. Priority lives only in `priority:*` labels
|
||||
(48 medium, 37 low, 12 high, 4 critical).
|
||||
- **Legacy IDs preserved and never reused** (`README.md:64-66`, "IDs … werden **nie
|
||||
wiederverwendet**; sie leben in Issue-Titeln weiter"): **23** issues carry a leading
|
||||
legacy ID (`^ID:`), across **23 distinct** IDs — no ID identifies two issues. Three
|
||||
further titles mention an ID belonging to another issue (`LABNET-02` in management#15,
|
||||
#26 and #27), which is the scheme used as intended: per `CLAUDE.md:92-93` the IDs
|
||||
"benennen den Gegenstand", so referring to one from another issue is a cross-reference,
|
||||
not a collision.
|
||||
- **Closed, not deleted** (`README.md:72-74`): 40 closed issues are present and readable
|
||||
alongside the 71 open ones.
|
||||
- Link hygiene in the management repo is likewise clean: of 161 links in its documents,
|
||||
**0 broken** (`analysis/data/links.tsv`); the two broken links in the whole corpus are
|
||||
malformed upstream Element changelog entries in `ThreadNet-Web`.
|
||||
|
||||
impact: The rule set is not aspirational. Where a rule is mechanically checkable and the
|
||||
tracker enforces it — a milestone field, a label — compliance is total. That is the
|
||||
strongest single argument for the migration: this project already works the way neckbeard
|
||||
assumes, and the failures found elsewhere in this report are concentrated exactly where no
|
||||
mechanism enforces anything (prose, branches, commit metadata, cross-document consistency).
|
||||
|
||||
root-cause pattern: **Where the tool can hold the rule, the rule holds.** The inverse of the
|
||||
pattern behind F-001, F-004, F-005 and F-010.
|
||||
|
||||
neckbeard mechanism: Preserved and extended by ADR-0002
|
||||
(`docs/adr/0002-in-repo-issues.md`) plus `schema.yaml:76-84`, which makes `status` an enum
|
||||
(`[open, in-progress, done, rejected]`) that `scripts/validate.py` enforces via
|
||||
`check_fields` — the file-level equivalent of "exactly one status label", but checkable in
|
||||
CI rather than by convention. Note the direction of loss to plan for: neckbeard's issue
|
||||
schema has **no milestone and no priority field**, and
|
||||
`docs/aar/2026-08-09-neckbeard-v1-creation.md` lists "priority field on issues (YAGNI,
|
||||
revisit via refinement)" among the ideas deliberately not adopted. This project uses both,
|
||||
on 100% and 101 of 111 issues respectively. Recorded here as field-test evidence, not as a
|
||||
proposal — see REPORT.md's gap list.
|
||||
@@ -0,0 +1,43 @@
|
||||
# F-015: The rewrite mapping list does what it promises — 251 of 251 rows verified
|
||||
|
||||
category: works-well
|
||||
confidence: high
|
||||
|
||||
evidence: `analysis/scripts/inv_shas.py` -> `analysis/data/sha_refs.tsv`, every row of
|
||||
`shared/commit-zuordnung-2026-08-07.md` re-checked against the actual repositories.
|
||||
- Row count matches the claim exactly: 117 (gitops) + 78 (management) + 47 (ThreadNet-Web)
|
||||
+ 9 (threadnet-call) = **251**, and `CLAUDE.md:136` says "251 Commits neue SHAs".
|
||||
- Of the 251 pairs: **209 `ok`** (new SHA resolves, old one gone as expected) and
|
||||
**42 `ok-both-present`** (new resolves, old still reachable — all 42 explained by the
|
||||
stale branches in F-006, not by a mapping error).
|
||||
- **0 inverted** (new missing while old resolves) and **0 unresolvable** (neither present).
|
||||
There is no row in the table that does not describe a real commit pair.
|
||||
- Consequently no accidental orphan reference exists in the docs: all six unresolvable SHA
|
||||
citations have individual, benign explanations (F-012).
|
||||
- The document is honest about its own boundaries in a way that measurably helped:
|
||||
`shared/commit-zuordnung-2026-08-07.md:39-44` states what is *not* in the table
|
||||
(pre-boundary commits, 147 in gitops, 3 in ThreadNet-Web) — which is what let this
|
||||
session classify unresolved SHAs correctly instead of reporting them as breakage.
|
||||
- It records the operational trap it hit rather than quietly fixing it
|
||||
(`:24-37`): the tag force-push restarted three release pipelines, and the reason nothing
|
||||
was overwritten is named as luck, not design — "**Das war Glück, keine Planung**" — with
|
||||
the follow-up tracked as ThreadNet-Web#14.
|
||||
|
||||
impact: A destructive, irreversible operation was made auditable after the fact by one
|
||||
hand-written document, and the audit passes three days later. The unusual quality is the
|
||||
self-criticism: the near-miss is written down as a near-miss, which is exactly the material
|
||||
a future session needs and exactly what usually gets smoothed over.
|
||||
|
||||
root-cause pattern: **A risky operation paired with a written reconstruction of what it
|
||||
did.** Note the limit, recorded as F-004's sibling: the `backup-vor-rewrite` branches the
|
||||
document names as its verification basis (`:14-17`) exist on **no** remote today, so the
|
||||
mapping can no longer be re-derived — only, as here, spot-checked against surviving refs.
|
||||
|
||||
neckbeard mechanism: `docs/aar/` is the home for this document type — `AGENTS.md:83`,
|
||||
"Standalone After Action Reviews (incidents, major deviations only)", with the required
|
||||
frontmatter at `schema.yaml:67-74` (`type`, `status` from `[open, harvested]`, `date`) and
|
||||
the content shape at `WORKFLOW.md:84-89` (planned / actual / why the difference /
|
||||
learnings, then harvest into the wiki). `WORKFLOW.md:105` makes it mandatory rather than
|
||||
optional: "Incidents and major misdiagnoses get a standalone AAR in `docs/aar/`". The
|
||||
`status: harvested` transition is the piece this project lacks — it writes AARs but has no
|
||||
mechanism that marks a learning as absorbed.
|
||||
@@ -0,0 +1,49 @@
|
||||
# F-016: The repo names its own unresolved problems instead of presenting a clean face
|
||||
|
||||
category: works-well
|
||||
confidence: high
|
||||
|
||||
evidence:
|
||||
- Known problems written down *and* filed as issues, verified present in
|
||||
`analysis/data/gitlab_issues.json`:
|
||||
- `hosts/overmind.md:33-35` — "⚠️ `gameserver` … hat **keinen** Mirror … dort liegt auf
|
||||
Gitea ein gleichnamiges Repo mit anderem Stand" -> management#32, open. **Verified
|
||||
true by measurement**: git.lab `d5c6ccb2e9` vs Gitea `48441a500f`, diverged (F-007).
|
||||
- `CLAUDE.md:63` — the gitops `wiki` branch is "ein überholter Mai-Abzug" -> management#19,
|
||||
open. **Verified**: head `0ff598e`, 2026-05-14 (F-006).
|
||||
- Mirror failure going unnoticed -> management#28 "MIRROR-01", open.
|
||||
- Restore never rehearsed -> management#30, "Der Restore ist nie geprobt — Sicherungen
|
||||
sind bisher eine Vermutung".
|
||||
- `CLAUDE.md:143-147` — the timestamp rule "**schützt nur die Git-Historie**", with push
|
||||
times, issue timestamps, pipelines and packages named as remaining exposure. Incomplete
|
||||
(F-002) but volunteered.
|
||||
- `CLAUDE.md:94-97` explains a rule by the damage its absence did, with numbers: 34 migrated
|
||||
issues carried title prefixes, "davon **zwei mit einer anderen Aussage als ihr Label**",
|
||||
and states the principle — "Zwei Wahrheiten über dieselbe Sache sind schlimmer als eine
|
||||
unvollständige."
|
||||
- `CLAUDE.md:149-158` ("Redlichkeit & gelebte Lehren") requires verified claims to be
|
||||
separated from suspicion, states "Korrelation ≠ Kausalität — ein plausibler Verdacht ist
|
||||
kein Befund", and closes with "Fehlschläge und übersprungene Schritte werden benannt,
|
||||
nicht geglättet; „fertig" heißt verifiziert."
|
||||
- `verfahren/retro/2026-08-09.md:70-71` records CI that was green while doing nothing
|
||||
(`build_embedded` "lud **nie** ein Artefakt hoch") and a published npm package of 12.5 KB
|
||||
instead of 12.8 MB — failures found and written down rather than quietly fixed.
|
||||
|
||||
impact: This is why the present analysis was possible at all. Most of the drift in this
|
||||
report was found by taking the repo's own statements seriously and checking them; that only
|
||||
works because the statements are specific, dated and falsifiable. A repo that wrote
|
||||
"Mirrors are configured" instead of "seven repos, these six product ones plus management"
|
||||
would have yielded no findings and no confidence either way.
|
||||
|
||||
root-cause pattern: **Specific, falsifiable claims are a feature.** Every finding in this
|
||||
report is downstream of a claim precise enough to be wrong.
|
||||
|
||||
neckbeard mechanism: This is `AGENTS.md:50-55` almost verbatim — "Never claim something
|
||||
works without evidence: a test run, command output, a rendered result. 'Should work' is not
|
||||
a status" and "Uncertainty is reported, never swallowed. Flag your shakiest calls." It
|
||||
recurs at `WORKFLOW.md:68` (Gate 3, "Shakiest calls: name the decisions you are least
|
||||
confident about") and `WORKFLOW.md:113-114` (every session ends by answering which choices
|
||||
it is least confident about). `WORKFLOW.md:137-139` completes it: "If the wiki has no
|
||||
confident answer, say so. Never file a low-confidence synthesis back as knowledge."
|
||||
The habit already exists here; neckbeard gives it fixed places to live rather than relying
|
||||
on the author's temperament.
|
||||
Reference in New Issue
Block a user