diff --git a/analysis/HANDOFF.md b/analysis/HANDOFF.md new file mode 100644 index 0000000..ab04a75 --- /dev/null +++ b/analysis/HANDOFF.md @@ -0,0 +1,126 @@ +# HANDOFF.md — Session 1 → Session 2 + +Everything Session 2 needs, and nothing else. Session 2 designs and builds the +neckbeard-based management system in a **new branch**, starting fresh from this file. + +## Where things are + +| | | +|---|---| +| Analysis branch | `analysis/neckbeard-fieldtest`, branched from `main` at `2f012a6` | +| Commits | `f6a9b9f` scope · `b8b8531` inventory · `959bf88` SHA + timestamps · `eaa00f0` findings · this file | +| Deliverables | [SCOPE.md](SCOPE.md) · [REPORT.md](REPORT.md) · [findings/](findings/) (F-001…F-016) · [data/](data/) · [scripts/](scripts/) | +| Read first | REPORT.md executive summary, then the pattern → mechanism → implication table | +| Neckbeard reference | `v0.1.1` @ `823a08cac6b03a47d7e2f661200a49ac6e09d38d`, `main`, clean | + +Nothing outside `analysis/` was modified on this branch. `main` is untouched. + +## Reproducing the raw data from zero + +```bash +git clone https://git.lab/axion1337.chat/management.git +cd management && git checkout analysis/neckbeard-fieldtest +bash analysis/scripts/run_all.sh +``` + +Clones the five component repos into `../components/` if absent, exports group issue +metadata from git.lab, regenerates everything under `analysis/data/`. Bash + python3 +stdlib only, no pip installs. Requires lab/VPN access for the clone and the issue export; +both skip cleanly without it and the rest still runs. Override the checkout location with +`NB_WORKSPACE`. + +**A second run on an unchanged tree must produce no diff.** Verified with +`diff -r` after a full regeneration. No wall-clock time enters any output: "days since" +is measured against the management repo's HEAD commit date (2026-08-09), not the clock. + +`inv_repo.py` aborts the whole run if anything outside `analysis/` was modified. That +guard was tested by deliberately dirtying `README.md`; it fires. + +## Scope, as frozen at the Phase-0 STOP + +Five components, slugs verified against git.lab with `git ls-remote`, human-confirmed: + +| Display name | Repo slug | +|---|---| +| ThreadNet Call | `threadnet-call` | +| ThreadNet Git | `thread-net-git` | +| ThreadNet Operating | `threadnet-operating` | +| ThreadNet Server Suite | `axion1337.chat-gitops` | +| ThreadNet Web | `ThreadNet-Web` | + +Out of scope as analysis targets: `game-operating`, `gameserver`, the `vendor` and +`Archiv` subgroups. References into them are recorded as `points-outside-scope`. +The human confirmed their own working copy points at canonical git.lab, not the mirror. + +## Decisions taken this session, and why + +1. **Both repos freshly cloned; push URL set to `DISABLED-no-push` on every clone**, + including clones `run_all.sh` creates. The mandate forbids modifying originals; making + it mechanically impossible beats remembering not to. +2. **The management repo is inventoried at `main`, not at the analysis branch.** The first + run counted this session's own commits and `analysis/` files as project reality (87 + commits instead of 86, 45 files instead of 44). An analysis that observes itself + reports its own footprint as drift. +3. **Commits follow the repo's own convention** (`CLAUDE.md:116-134`): English, + conventional style, author *and* committer date at 12:00:00 UTC, plus the mandated + `analysis:` prefix. Not anonymising would have been drift this session introduced. +4. **The GitLab token is referenced by path, never read into output.** Per `CLAUDE.md:109` + its value was not printed, logged or written anywhere; the export holds **metadata + only** — no issue descriptions, no comments. +5. **All group issues exported, open and closed**, rather than an arbitrary + "recently closed" cutoff. The group is small (111 issues); a full export is + deterministic and needs no justification for where the line was drawn. +6. **`sha_refs.tsv` and `timestamp_anonymisation.tsv` were added** beyond the mandated file + list, prompted by the human's question about the rebase mapping list. They carry the + evidence for F-002, F-003, F-006, F-012 and F-015. +7. **Two tooling bugs were found and fixed mid-flight**, both of which would have produced + false conclusions: `git grep -E '\b…'` silently reports **zero** markers because git's + ERE has no `\b` (real count: 543), and a `TZ` that is not set on the `git` process + itself makes every commit look non-anonymised. +8. **Link statuses stayed at the three mandated values**; the nuance moved into a `kind` + column rather than inventing a fourth status. + +## Where Session 2 should start + +The pattern → mechanism → implication table in REPORT.md is the input. Two things gate +everything else: + +- **Question 1 in REPORT.md** — where the 71 open issues live after migration. ADR-0002 + Option C versus pure Option B changes the shape of the whole design. +- **Neckbeard gap 1** — how five repos share one `AGENTS.md`. Unanswered, this blocks any + multi-repo adoption. + +Neckbeard's own rules for Session 2: `PROJECT.md` is absent from the management repo, so +**Gate 0 fires there** (`AGENTS.md:57-63`) — it was suspended for this session only. The +migration is size L, so `WORKFLOW.md` Gates 1–5 with a design doc in `docs/design/` apply. + +## The decisions I am least confident about + +In descending order of how much they could mislead Session 2. + +1. **Claim extraction is trigger-based, not semantic.** `claims.tsv` holds 813 candidate + lines matched by regex patterns, biased toward recall. I verified the claims the + mandatory checks and the findings required — not all 813. A claim that is false but + matched no trigger, or matched one and was not among the ones I chased, is not in this + report. This is the largest hole in the analysis. +2. **`git ls-remote` against `rohana.axion1337.de/sorb/` is how I concluded + `game-operating` has no mirror** (F-007). That rests on the naming convention holding, + which I verified for six repos but cannot prove for the seventh. The contradiction + between `CLAUDE.md:36` and `hosts/overmind.md:28` is solid; the cause is not. +3. **F-009 and F-010 are judgement calls, marked `medium`.** "Doc attention should follow + work mass" assumes dormancy is unintended, and a "Stand" label may legitimately mean + "system state as last verified" rather than "state of this text". Both could be + dismissed by an answer I do not have. +4. **The severity I assigned to F-002/F-003.** I treated 237 commits with real clock times + on a public mirror as the report's most consequential finding. That weighting is mine. + If the exposure is acceptable to you, the finding shrinks to a documentation defect and + the executive summary over-weights it. +5. **F-011's confidence is `medium` because the sentence is ambiguous.** "Projekt-Repos + haben eigene CLAUDE.mds" may be descriptive rather than a claim about all five. I read + it as a claim because the sentence goes on to define conflict resolution against it. +6. **I did not verify claims made inside issue bodies.** `CLAUDE.md:52` says the migration + footnote *in the issue* is authoritative for shifted gitops numbers — so the one source + that could settle F-005's numbering question is the one I deliberately did not export. +7. **`markers.tsv` was produced and then barely used.** 543 markers, 538 of them inherited + upstream. I recorded "management: 0 markers, checked, no drift found" and moved on. If + technical debt in the components matters to Session 2, that file is unmined. diff --git a/analysis/REPORT.md b/analysis/REPORT.md new file mode 100644 index 0000000..574334d --- /dev/null +++ b/analysis/REPORT.md @@ -0,0 +1,190 @@ +# REPORT.md — Neckbeard field test on the axion1337.chat management repo + +Session 1 of 2, analysis only. Measured against neckbeard **v0.1.1** +(`823a08cac6b03a47d7e2f661200a49ac6e09d38d`, `main`, clean) as pinned in +[SCOPE.md](SCOPE.md). Raw data reproducible from zero with +`bash analysis/scripts/run_all.sh`; 16 findings in [findings/](findings/). + +--- + +## Executive summary + +The management repo is 11 days old, holds 44 files and 86 commits, and steers five +component repos plus itself. Its home-grown approach is **not decayed — it is unevenly +enforced**. Where a tool can hold a rule, the rule holds perfectly: every one of 71 open +issues has exactly one milestone and at most one status label, the WIP limit is respected, +no legacy ID is reused across 23 issues, and the management repo's 161 document links +contain zero broken targets. Where nothing enforces anything — prose, git refs, commit +metadata, agreement between two files — the same rule set fails, and it fails in the same +four ways every time. + +The most consequential finding is not documentation drift. The 2026-08-07 history rewrite +set 251 commits to 12:00 UTC so that personal working hours could not be read out of the +history. That goal is met in **one of six repos**. 237 commits by the project's own +identities still carry real clock times, including 41 on an abandoned branch in +`axion1337.chat-gitops` clustered at 21:00–01:00 — publicly readable on the Gitea mirror, +which this session verified is in sync. Two component repos were never rewritten at all. +The rule's own caveat in `CLAUDE.md:143` lists push times, issue timestamps and pipelines as +residual exposure, but not the git history itself, so a reader concludes the opposite of +what is true. See [F-002](findings/F-002-timestamp-anonymisation-covers-one-of-six-repos.md), +[F-003](findings/F-003-author-identity-unification-incomplete.md), +[F-006](findings/F-006-stale-branches-publish-pre-rewrite-history.md). + +Second: a decision that was made, executed and is already load-bearing exists in no +steering document. Milestone **M5 — Härtung** was created on 2026-08-09 with 14 issues moved +out of M1. `roadmap.md`, committed the same day, still poses it as an open question and +still prints the pre-split distribution; `CLAUDE.md:98` still binds every issue to "M1–M4"; +no ADR covers it, though the repo's own rules make one mandatory for process decisions. An +agent following the canonical file would silently undo the split. +See [F-001](findings/F-001-milestone-m5-decided-but-absent-from-steering-docs.md). + +What must survive migration is substantial and specific. The mirror topology is documented +with its reasoning, its counter-argument, its rescue procedure and its single justified +exception — and all six mirrors measure in sync. The rewrite mapping list verifies 251 of +251 rows against the live repositories, with zero contradictions. The repo names its own +unresolved problems, files them as issues, and records a near-miss as luck rather than +design. That candour is why this analysis was possible at all: every finding here is +downstream of a claim precise enough to be wrong. + +**Bottom line for Session 2.** This project already works the way neckbeard assumes. The +migration's value is not new discipline but moving the existing discipline from places +where only a human can check it into places where a script can. Four framework gaps stand +in the way, listed below; the sharpest is that ADR-0001 solves one-repo-many-harnesses and +this project is many-repos-one-ruleset. + +--- + +## Findings by root-cause pattern, most impactful first + +### Pattern B — A rule is declared repo-wide and applied to a subset + +The declaration and the application live in different places, and nothing compares them. + +| Finding | | Impact | +|---|---|---| +| [F-002](findings/F-002-timestamp-anonymisation-covers-one-of-six-repos.md) | Anonymisation reaches 1 of 6 repos; 237 commits expose real hours | Stated privacy goal unmet, publicly; expensive operation largely undone | +| [F-003](findings/F-003-author-identity-unification-incomplete.md) | 5 author identities of one person survive, one malformed | Same exposure; one person shows as 4–5 contributors publicly | +| [F-011](findings/F-011-component-claude-md-claim-unmet.md) | 4 of 5 components have no `CLAUDE.md` | The distribution channel for the rules is itself only partly applied — a plausible cause of F-002/F-003 | +| [F-008](findings/F-008-component-slugs-inconsistent.md) | Five components, four naming schemes | No deterministic path from display name to repo; this session had to ask | + +### Pattern A — A decision is executed in the tool; the documentation is not carried along + +| Finding | | Impact | +|---|---|---| +| [F-001](findings/F-001-milestone-m5-decided-but-absent-from-steering-docs.md) | M5 decided and populated; docs still call it open | Every new session is mis-primed by the canonical file | +| [F-007](findings/F-007-mirror-scope-claims-contradict-each-other.md) | Mirror count stated as five and six on the same day; `game-operating` claimed mirrored, is not | Gap in the availability guarantee the two-host topology exists for | +| [F-005](findings/F-005-dead-gitea-tracker-still-referenced.md) | Live doc routes to the tracker three docs declare dead, under an ambiguous number | A high-priority open decision is reachable only through a retired system | +| [F-010](findings/F-010-stand-labels-lag-their-own-commits.md) | Hand-written "Stand" labels older than their own file's last commit | Trains readers to distrust current content; gives no signal when content really is stale | + +### Pattern C — Two backlogs, one rule + +| Finding | | Impact | +|---|---|---| +| [F-004](findings/F-004-open-work-lives-only-in-host-markdown.md) | Open next steps only in `hosts/`; OVERMIND-01, CFGMON-11/12/13, MATRIX-05 have no issue | Items invisible to board, milestone, priority and refinement | +| [F-009](findings/F-009-doc-attention-does-not-follow-work-mass.md) | gitops holds half the backlog and is absent from the vision | Session-start reading mis-weights prioritisation | + +### Pattern D — Artifacts outlive their purpose without an owner + +| Finding | | Impact | +|---|---|---| +| [F-006](findings/F-006-stale-branches-publish-pre-rewrite-history.md) | 3 abandoned branches, one public with 41 pre-rewrite commits; `wiki` branch is a 3-month-old parallel truth | Undoes the rewrite for 42 commits; two truths about the same docs | +| [F-012](findings/F-012-orphaned-sha-references.md) | 6 unresolvable SHA citations, all benign — but nothing could check them | Evidence-by-citation depends entirely on human diligence at rewrite time | + +### What works and must be preserved + +| Finding | | Why it must survive | +|---|---|---| +| [F-014](findings/F-014-issue-hygiene-is-real.md) | Milestone duty 100%, one status label 100%, WIP limit held, no ID reuse, 0 broken links | Proof the team already works the way neckbeard assumes | +| [F-013](findings/F-013-mirror-topology-actually-holds.md) | All six mirrors in sync; decision recorded with reason, counter-argument, rescue path, one bounded exception | The form is the value: this is an ADR in all but frontmatter | +| [F-015](findings/F-015-rewrite-mapping-is-sound.md) | 251/251 mapping rows verified, 0 contradictions; near-miss recorded as luck | Shows a destructive operation made auditable by hand | +| [F-016](findings/F-016-self-documentation-is-honest.md) | Known problems named and filed; "zwei Wahrheiten … schlimmer als eine unvollständige" | Falsifiable claims are what made this analysis possible | + +--- + +## Pattern → neckbeard mechanism → migration implication + +| Pattern | Neckbeard mechanism (v0.1.1) | Implication for Session 2 | +|---|---|---| +| **B** — rule declared repo-wide, applied to a subset | **Largely a gap.** ADR-0001 (`docs/adr/0001-agents-md-canonical.md`) removes per-harness duplication *within* one repo; `schema.yaml` filename patterns + `validate.py` ("filename does not match", "id does not match filename prefix") show the framework already holds that names must be mechanically derivable | Decide **how five repos share one `AGENTS.md`** before anything else — vendoring copies re-creates the drift ADR-0001 rejects; a cross-repo link breaks ADR-0003 portability. Then introduce a components artifact that names each repo's canonical slug, so F-008 becomes checkable | +| **A** — decision executed, docs not carried along | `AGENTS.md:79` + `scripts/gen_status.py`: `STATUS.md` is generated, "do not edit by hand". `AGENTS.md:81`: ADRs binding, "never edited, only superseded". `WORKFLOW.md:128`: STATUS review on the refinement agenda. `WORKFLOW.md:136-138`: "Contradictions are resolved or explicitly flagged — never left silently coexisting" | Replace every hand-written count and "Stand" label with generated output. Port `decisions/0001-0009` to `docs/adr/` with `schema.yaml` frontmatter — they are already ADRs in substance. **Then file the missing one for M5.** | +| **C** — two backlogs, one rule | ADR-0002 (`docs/adr/0002-in-repo-issues.md`): issues as `docs/issues/NNNN-slug.md`, status in frontmatter, `STATUS.md` as index; `schema.yaml:76-84` enum `[open, in-progress, done, rejected]` enforced by `validate.py` `check_fields` | The **biggest decision of Session 2**: 71 open issues currently live in GitLab with milestones, `priority:*` labels and a group board. ADR-0002 Option C (repo canonical, mirrored to the forge) exists precisely for this and is the only option that does not throw away F-014's proven discipline. Do not migrate by deleting the board | +| **D** — artifacts outlive their purpose | **Gap for refs.** `schema.yaml`'s `warn_if_orphan` shows "reachable from nowhere" is already an accepted reportable condition — for documents. `WORKFLOW.md:122,126`: refinement backlog triage and wiki lint | Add a branch/ref hygiene step to the refinement ritual, and a SHA-resolution check (`analysis/scripts/inv_shas.py` is a working reference implementation) | + +--- + +## Neckbeard gaps this field test exposed + +Listed only, not filed as issues in the neckbeard repo — that is a separate act. + +1. **Many repos, one ruleset.** ADR-0001 decides one-repo-many-harnesses. Nothing says how + a five-component project shares a governing `AGENTS.md` without either vendoring copies + (the drift ADR-0001 rejects) or cross-repo links (which ADR-0003 portability disallows). + Sharpest gap found. → F-011 +2. **No components artifact.** No artifact type declares "these are the repos and these are + their canonical names", so slug drift and cross-repo status are unrepresentable. + `gen_status.py` covers one repo. → F-008, F-009 +3. **No milestone or delivery-bucket concept.** `schema.yaml` has no field grouping issues + by what they pay into. This project uses milestones on 100% of open issues. → F-001 +4. **`validate.py` does not resolve commit SHAs cited in prose.** `check_body_links` covers + markdown link targets only. Resolving a cited SHA is a textbook deterministic job under + `AGENTS.md:101-103`. → F-012 +5. **No git-level hygiene at all** — commit metadata, author identity, branch lifetime are + outside the framework's field of view, while in this project they carry its most + sensitive claims. → F-002, F-003, F-006 +6. **External link targets are never checked.** A document pointing at a retired external + tracker stays green. → F-005 +7. **Field-test evidence on a deliberately rejected idea.** The creation AAR lists "priority + field on issues (YAGNI, revisit via refinement)" among ideas not adopted. This project + carries `priority:*` on 101 of 111 issues and treats it as distinct from the milestone + ("`priority:*` sagt **wie dringend**, der Meilenstein sagt **worauf es einzahlt**", + `CLAUDE.md:99-101`). Recorded as evidence for the refinement that ADR-flagged revisit, + **not** re-proposed here. → F-014 + +--- + +## Open questions for the human + +1. **Where do the 71 open issues go?** ADR-0002 Option C (repo canonical, mirrored to + GitLab) preserves the board, the milestones and the labels that F-014 shows are working. + Pure Option B is cleaner and loses all three. This decides the shape of Session 2. +2. **F-002/F-003 are a live exposure, not a documentation defect.** Do you want the + remediation treated as an incident now — separately from and ahead of the migration? + This session recorded it and changed nothing, per mandate. +3. **`game-operating` mirror**: is it absent, or does it mirror under a name other than + `sorb/game-operating`? The negative result rests on the naming convention. +4. **Are `thread-net-git` (4 files) and `threadnet-operating` (0 issues, 7 days idle) + dormant by design?** If yes, the vision document should say so; if no, they are + under-tracked. Either way the docs currently imply five equal product lines. +5. **The M5 ADR** — file it as ADR-0010 in the current scheme now, or let it land as the + first ADR in the migrated `docs/adr/`? + +--- + +## Non-goals — what this analysis deliberately did not examine + +- **No design doc in the target repo.** Deliberate deviation from `WORKFLOW.md` size-L + Gates 1–5: the migration design belongs to Session 2. Analysis precedes adoption. +- **Gate 0 suspended.** `PROJECT.md` is absent from the management repo, which under + `AGENTS.md:57-63` would be the init trigger. Suspended by the session mandate; recorded + as an observation, not a task. +- **Component code read structurally only** — trees, manifests, CI files, entry points. + No depth reading; no finding here required it. +- **Upstream fork history not analysed as project drift.** `threadnet-call` and + `ThreadNet-Web` carry thousands of Element/Matrix commits and 538 of the 543 + TODO/FIXME/HACK markers found. Those are inherited, not steering artifacts. The + management repo itself contains **0 markers** — checked, no drift found. +- **Issue descriptions and comments were not exported.** `gitlab_issues.json` holds + metadata only, by design. Claims made *inside* issue bodies are therefore unverified — + including the migration footnotes `CLAUDE.md:52` says are authoritative for shifted + gitops numbers. +- **Out-of-scope repos** — `game-operating`, `gameserver`, the `vendor` and `Archiv` + subgroups — analysed only where a management-repo claim referred to them. Their contents + were not examined. +- **The `homelab` group** (`docs`, `wiki`, `wiki-bookstack`) and **axionwiki.lab** were not + examined; cross-references to them are recorded as `points-outside-scope` in `links.tsv`. +- **CI behaviour was not exercised**, only configuration presence (e.g. `canonize_rotation` + verified to exist at `.gitlab-ci.yml:51` in gitops; not observed running). +- **The human's own working copy** could not be inspected. The human confirmed at the + Phase-0 STOP that it points at git.lab. +- **Nothing was fixed.** No file outside `analysis/` was modified; `inv_repo.py` aborts the + run if any was, and the guard was tested by deliberately dirtying `README.md`.