ThreadNet-Web:main auf 8ca03fe, Merge-Commit 88c4e15 mit beiden echten Eltern.
GHSA-wrcp-5v3v-3j6v ist mit dem Versionssprung erledigt.
Drei Anlaeufe: rc.1 erzeugte kein Image (.npmrc fehlte im Docker-Kontext, unter
pnpm 11 fatal), rc.2 ging live und brach die Raumliste, rc.3 bestand die Abnahme.
ADR-0022 um zwei Korrekturen ergaenzt, die erst die Ausfuehrung gezeigt hat:
Die stille Klasse verschwindet nicht ganz, sie dreht sich um. Der Merge meldet,
wenn Upstream eine Datei verschiebt - das hat gehalten. Er meldet nicht, wenn
beide Seiten die Aufloesung ueberleben und nur eine noch Sinn ergibt. Genau das
brach rc.2.
Und: ein gruener Build war nie eine Abnahme. Der web-Job baut nur, webpack
wirft Typen weg. tsc meldete den Fehler durchgehend, gefragt hatte ihn niemand.
Seit 8ca03fe fuehrt docker_web den Job typecheck als needs.
Daraus die stehende Regel fuer kuenftige Merges: nach der Konfliktaufloesung auf
ueberlebende Reste pruefen, nicht nur auf verlorene Zeilen. Wo Zeichenketten
statt Typen im Spiel sind, braucht es einen Abgleich gegen die Registry - fuer
Einstellungen einmalig gefahren, 135 abgefragte gegen 152 registrierte.
Decision sorb: option B, a one-time real merge rather than a shared replace ref. What
decided it was visibility, not effort - a replace ref works only while everyone
remembers to fetch it, and for a repo whose core problem is "git says nothing", a
mechanism that silently differs per clone is the wrong shape.
The test corrected the option's own description. --allow-unrelated-histories on its own
gives a two-way comparison and 1757 conflicts; with the graft set locally it is 32. So
the graft is not the alternative to B, it is how B is performed: set it, let the merge
compute against it, commit, and the merge commit then carries the real parents so the
graft can go.
Also recorded because it cost time and looked like a fundamental problem: tags fetched
with --depth=1 leave a shallow boundary, so v1.12.26 was walled off at a single commit
even though develop carried the same commit in full. Every merge attempt failed with
"refusing to merge unrelated histories" until fetch --unshallow.
The merge itself is measured but deliberately not executed. apps/web/package.json
carries two product decisions rather than conflicts - our Element Call fork against
upstream's, and a matrix-js-sdk git pin against a released version - and resolving the
first one wrongly would silently delete the noise suppression work from #0054. Neither
is safe without a build and the ClamAV functional test.
sorb chose C, fully disabling federation at the edge. Building it showed that would
have killed group calls: lk-jwt-service verifies OpenID tokens through
/_matrix/federation/v1/openid/userinfo and reaches it over the public name, so a path
block on /_matrix/federation is the mrtc outage again with a different cause. C was
dropped and B implemented.
The ADR records the measurement the decision rests on - zero destinations, zero remote
users, zero rooms with outside participation in four months - and the trap, so nobody
completes C later as a quick follow-up. Doing that safely means binding the auth
service to Synapse in-cluster first, which is its own undertaking with a call
acceptance.
Also recorded because it nearly slipped through: Flux applied the ConfigMap while
Synapse kept running its old config from 2026-08-01. The config is rendered at pod
start, so the change was inert until a restart - the same class as #0044. Verified
afterwards inside the running process rather than in the ConfigMap, with the client
API and the OpenID endpoint still answering.
The framework audit found this missing. Acknowledging known findings is a process
decision about how the alarm system treats exceptions, and AGENTS.md is explicit
that documenting an exception instead of deciding it is itself the error. It lived
only inside #0104 and #0105, which are issues, not decision records.
The ADR carries the reasoning the issues could not: why option C beat working the
backlog down first or tolerating red, and why the obvious objection - an exception
list is a candidate for the next blind spot - is answered by the three rules rather
than waved away. It also records what is deliberately not acknowledged, the
transient mirror divergence, because that message is the only signal if a mirror
truly stops.
One consequence is stated plainly rather than discovered later: acknowledgements
bind to substrings of the finding text, so fixing or moving a cause can change the
wording and require the entry to follow. Stable finding IDs would avoid that and
would make the file unreadable without special knowledge; the trade is taken
knowingly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decision sorb. ADR-0002 held that all project issues live on git.lab. Since
ADR-0012 and now ADR-0019 the canonical place is docs/issues/ in this repo, with
GitLab as the generated mirror - so that sentence stood in the record as a valid
rule next to its own opposite.
Only that sentence falls. ADR-0002 also moved the backlog repo into the lab and
named the lab the source of truth; git.lab stays canonical for code (ADR-0001) and
the repo stays where ADR-0002 put it. Because a bare "superseded" would read as if
the move were undone too, ADR-0019 now carries a section drawing the line, and the
superseded_by pointer leads a reader there. ADR-0002's body is untouched, matching
how ADR-0006 and ADR-0007 were retired.
ADR-0005 is deliberately left accepted: the WIP limit, the status labels and the
rule that only sorb promises work all still hold - only "the board is the truth"
moved, and ADR-0012 already records that.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ADR-0012 made docs/issues/ canonical for the management scope only and left
gitops, ThreadNet-Web and threadnet-call on GitLab "until the component adopts".
That split produced exactly what it invited: two numbering worlds where
management#20 and gitops#20 are different issues, drift nobody had to answer for
(gitops#61 carried no milestone since 2026-08-11), and component backlogs that
host sessions without lab access cannot read at all.
The 46 open component issues are now files 0056-0101. The file id is the
group-wide identifier; provenance lives in the frontmatter (new field `projekt`
plus gitlab_iid) and in the filename, so "gitops#61" still finds 0091. Bodies are
copied verbatim; comments and history stay on GitLab, as with the 2026-08-11
management import.
Both scripts learned the second dimension: spiegel_issues.py routes each file to
its origin project, reopens issues that are open in the repo but closed on the
board, and writes the new iid back after creating one; gruppenpruefung.py checks
drift across all four trackers instead of management alone. What the mirror
cannot decide stays a finding, not a silent state.
Two things needed a hand, both recorded in the files: gitops#61 had no milestone
(M1 - it is a live account-takeover path) and carried two area labels where the
schema holds one. The Gitea migration footers in the imported bodies point at
decommissioned trackers; their links are removed, the provenance sentence stays.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
sorb's decision after the prototype: integrate DeepFilterNet3 as a LiveKit track
processor, off by default, assets fetched only when the user enables it, checkbox
plus slider, 35 percent default.
Opt-in is what makes the 23.3 MB affordable — only those who benefit pay for it.
Three of the source specification's assumptions did not survive measurement and are
recorded as rejected alternatives: the dry/wet mixer (the model limits attenuation
natively, and mixing raw signal back would return the keystrokes), the Rust/wasm
build (a maintained package makes it unnecessary), and loading assets from the
vendor CDN (every participant's IP to a third party at call start).
Mobile stays untested by choice; since the filter is opt-in it simply stays off on
weak devices, so that is a follow-up rather than a blocker.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ADR-0006 (Docusaurus as the shared reading surface) is superseded by ADR-0014,
which ADR-0014 had only recorded for ADR-0007. The schema has no 'deprecated', so
superseded with a pointer is the fitting lifecycle state, same shape as ADR-0007.
#0054 evaluates an external architecture spec for filtering keyboard noise with a
WebAssembly model in the client. It holds up on diagnosis, placement and the
awkward parts (128-vs-480 sample buffering, the Chromium worklet leak, SIMD), and
it does not contradict the fork's earlier rejection of ML denoising — that one was
about the server side, for a reason that does not apply here.
It does not hold up on: a missing delay node, which would make the dry/wet mix comb
filter audibly; the premise behind dry/wet at all, since DeepFilterNet can limit
attenuation natively and mixing raw signal back in returns the very keystrokes we
want gone; PESQ figures compared across different test sets; unmeasured bundle size;
throwaway npm packages; and no mention of the standing cost of carrying this through
every upstream rebase.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Corrects only the split-DNS line of ADR-0004 (frozen once accepted, hence a
separate ADR). Rather than documenting 'four zones exist', it measures what each
one does: ~lab and ~axionlabs.de resolve names that exist only internally or
differently (git.lab, and ca.axionlabs.de as real split-horizon to the step-ca),
~axion1337.de carries the internal-only git.axion1337.de, and ~lab.de carries
nothing at all while routing a foreign public domain through the lab resolver —
so it goes.
This also answers the audit's rollback option: reverting to ~lab alone would have
broken internal CA and git resolution. The purpose was never written down, which
is why rolling back would have been blind.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Standing exception to ADR-0001 (everything is push-mirrored to Gitea). The
handbook necessarily maps the infrastructure, the backup locations and where the
keys are kept; mirroring it onto the internet-facing host that is itself one of
the covered failure cases would hand a post-compromise attacker their next step.
Confidentiality over availability, with a local clone closing the availability
gap. Records the rejected alternatives so the question does not reopen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wiki.js→Gitea (sorb/ThreadNetWiki) is wired and verified end-to-end (status
operational, page create/delete propagates). Move ADR-0015 to accepted with an
implementation note; mark the git-storage part of #0048 done. Remaining: the
canonize job Gitea→git.lab (needs the target repo decision).
The cluster cannot reach git.lab (deliberate lab-independence), so #0048's
git.lab-repo-as-storage is infeasible. ADR-0015 routes Wiki.js content
cluster→Gitea→canonize to git.lab, reusing the accepted TURN-rotation pattern;
status proposed, pending sorb's ratification and two prerequisites (Gitea repo +
deploy PAT). Note the flagged contradiction on #0048 and the live theming
progress (logo, shared login background, dark default) on #0050. STATUS regen.
ADR-0014 records the decision: Wiki.js replaces Docusaurus for the platform wiki
— the only option meeting both hard requirements (per-group abschotten AND
docs-as-code in git). BookStack ruled out (DB-only, no git). Scope excludes
homelab/docs; neckbeard docs stay in management; content in a dedicated wiki repo
(not a branch, not a monorepo). ADR-0007 set to superseded. #0047 resolved
(decided: Wiki.js). Build issues 0048 (deploy + git storage), 0049 (OIDC + roles/
abschottung: admins write, users read-only), 0050 (theming, colours+logo extracted
from homelab/wiki). #0046 becomes the umbrella. STATUS regenerated; all gates green.
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>
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>
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>
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>
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>
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>
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>