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>
The five-day group-call outage from the deleted mrtc A record had no management
record at all - the fix, the diagnosis path, and the lesson lived only in the
session. The AAR records why nothing alarmed (DNS-01 certs and pods stay green
without an A record), the exact dating via token-vs-join counts, and the
countermeasure that already shipped (notfallhandbuch dns-soll.md + pruefe-dns.sh).
gruppenpruefung's nine real-timestamp findings join #0053's history pass -
same class, same decision, recorded so the next session repairs nothing
unilaterally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decision sorb: the filter is live in v0.5.4, proven in real calls on both
engine families, gated for rollback, and regression-tested on all three silent
failures found along the way. The phone test stays deliberately deferred; if it
becomes necessary it is a new issue, not a reopen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ThreadNet-Web resolves @sorb/threadnet-call-embedded from rohana only because
pnpm-lock.yaml pins the full tarball URL and CI installs frozen. There is no
.npmrc anywhere, so the moment someone bumps the version, pnpm reaches for
registry.npmjs.org instead. Hit while bumping to .8 for #0054.
Today that fails loudly with a 404 — but only because the name happens to be
unregistered on public npm. The protection is a coincidence, not a control:
register that name and the same command resolves successfully against a
stranger's package, in the one moment where a fresh download looks expected.
Documenting it is explicitly not the fix here; the checked-in .npmrc is, because
it removes the wrong path rather than warning about it.
Co-Authored-By: Claude Opus 4.8 <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>
Route B per sorb: Draupnir would have needed server admin to poll reports, and
bots do not get that. So reports stay in event_reports for review through Element
Admin, and the message names a person rather than promising an automatism —
@sorb being the only admin who can see them at all.
Verified live rather than assumed: the config parses, the ConfigMap carries it,
the chart hash label flipped after about 70 seconds and rolled a new pod, and the
public config.json serves the text. That rollout also confirms the #0044 analysis
empirically — no reloader needed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#0018 builds entirely on the Docusaurus aggregate that ADR-0014 replaced, and
sorb confirms wiki.lab is gone — measured, it resolves but answers nothing, so
the Dokploy stack it asks for was never deployed. Its one live part was step 6:
gitops still claimed the docs were served there, corrected in gitops 82412cf.
#0028 turned out to be built already, on the very path the issue proposed:
stillstandspruefung.py reads remote_mirrors and reports last_error, CI runs it on
schedule, and the git.lab schedule is active daily at 00:42 — so an expired mirror
credential goes red within a day instead of freezing production silently. It also
demonstrably fires: it flagged three repos without an active mirror today.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On sorb's instruction. Six of the eight contradictions stand resolved; W5 and W7
went back to open when the unauthorised AGENTS.md edits were reverted, and both
now need a decision on whether and where the rule is recorded rather than just a
wording.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sorb's call. The goal is already met where secrets actually move: the ESS chart
rolls its components on config change via pod-template hash labels, and coturn
plus Synapse are handled by the rotation job's annotation bump — the one case with
regular unattended rotation, solved precisely because of that.
What is left are three services whose secrets change rarely and by deliberate act,
at the very moment someone is already watching and ADR-0011 applies. A permanent
controller allowed to patch arbitrary deployments is the worse trade for that.
The map is the outcome here, not an installation: the assumption had been that MAS
was uncovered, and a reloader would have been aimed at a solved problem.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#0043: the case-insensitive username policy is live and verified end to end —
present in the ConfigMap, mounted in the worker, applied by authentik on its own,
and bound to the prompt stage. It reads only prompt_data, since the stage runs
anonymously and that is exactly what the previous system policies died on.
#0044 turns out to be largely solved already, which the issue could not know: the
ESS chart hangs config and secret hashes on the pod template as labels, so MAS and
the other chart components do roll out on change, and coturn has its own annotation
bump driven by the rotation job. What remains are three services whose secrets
change rarely and deliberately — recommending against adding a controller for that.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Eight commits from the past two days carry the wrong author identity, made in an
agent session that set user.email by hand in fresh clones — an hour after that
same session wrote the canonical identity into AGENTS.md.
sorb's call is to fix them with the next history pass rather than force-pushing
two repos over eight commits. The issue exists anyway because gruppenpruefung
reports them on every run: without a recorded reason the next session starts
'repairing' them, or worse gets used to red findings, which is exactly what
happened with the TargetDown noise in #0002 the same morning.
Notes the structural prevention too — an includeIf block setting the identity for
group clones — since writing the rule down demonstrably did not prevent breaking it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The cluster was running coturn 4.10.0 while :latest pointed at 4.17.2, which is
the concrete harm the issue describes: nobody knew what ran, a reschedule would
have jumped seven minor versions unannounced, and the CVE scan was measuring a
moving target. Now pinned to 4.17.2 and verified beyond 'the pod is up' — a STUN
binding request from the public internet succeeds and the server reports the
caller's external address, so the relay path itself is proven.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
All five components now carry the pointer the check looks for, verified by
gruppenpruefung.py dropping from 27 to 23 findings — exactly the four pointer
findings. Each AGENTS.md carries only what is specific and easy to get wrong
there: for the forks, that the README is upstream material describing something
else entirely; for thread-net-git, that its small compose file hosts the Flux
source; for threadnet-operating, the two lessons this session paid for.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
W4 point 4 closed with gitops 60aaf0e: the lab WireGuard config now has a repo
home. The root CA turned out to already have one (ci/lab-ca-chain.crt is exactly
the aXionLabs chain), so that half of the point was quietly already met.
All eight now carry a named resolution with a reference, two of them as their own
ADRs, honouring this issue's own rule of documenting rather than silently fixing.
The only thing left is the rotation, which is dated follow-up work in #0015 rather
than an open contradiction.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sorb's call: rotate everything once at acceptance rather than piecemeal now.
That is the lower-risk order — the mirror credential is still unidentifiable and
the mirrors feed the Flux source, so four separate revocations would mean four
separate ways to break it silently. The inventory and the ordering stay valid, so
the later rotation is execution rather than analysis.
Recorded what the deferral accepts rather than leaving it implicit: the exposed
WireGuard key and PATs stay valid, five never-used tokens remain (one with
manage_runner and k8s), and 'acceptance' is not a dated milestone — which is
exactly how security work rots. The existing due date stays as a review anchor,
not a rotation deadline.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Inventoried the 24 git.lab PATs by metadata only — last_used_at separates
'needed' from 'lying around': four are in active use, five are active but never
used at all (one with manage_runner and k8s scope), and several names exist twice
because a replacement was created without revoking the old one. All six push
mirrors are healthy, but GitLab masks both parts of the mirror URL, so the
credential remains unidentifiable — and it is a Gitea token, which the PAT list
cannot answer for. Hence the ordering: set a dedicated mirror credential first,
revoke second. The revocations themselves are sorb's; from here a never-used
token is indistinguishable from a staged one.
W5 resolved: the secrets rule now has a bootstrap exception, since on a headless
host it was only satisfiable by violating it. W4 splits — point 5 is #0015 (plus
the WG key, which no token inventory covers), point 4 is demonstrably undone.
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>
Went through the seven imported waiting issues and replaced the generic
'reason is in the GitLab history' placeholder with the real blocker, which
completes #0041. Three of the seven were not merely imprecise but wrong:
- #0025: the deploy had long landed; screenshots confirm 24 aggregated messages
in the security room (limit 29), summing to the known 126 CRITICALs.
- #0014: the A/B/C decision exists as ADR-0008 (option A). Half its open question
is now answered — MATRIX has no docker group at all, so the root-equivalence
does not apply there.
- #0027: the blocking Struktur-Workshop happened on 2026-08-06 and produced three
ADRs, but W1 and W3 were spot-checked and are still unresolved.
The remaining four wait on a named action by sorb. Measured from here: the GAME
exporters are still filtered (and their silences expired on 2026-08-04, so
TargetDown has been firing every 4h since), while CFGMON's 9090/3100 are already
closed from the internet — so #0008 is about making that state deliberate rather
than an acute exposure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The handover issue still sat in waiting while the deploy had long landed:
ff87cb2 is an ancestor of HEAD (CFGMON now runs e9c13dc), the rules aggregate
per image so the per-CVE flood is structurally impossible, matrix-alerts.py
saves state incrementally inside the send loop, and notifications_failed_total
is 0 across 80 series. The null-receiver kill switch is gone.
Recorded honestly what was not observed: whether aggregated messages actually
arrived in the security room once. Delivery is now permanently monitored via
AlertDeliveryFailing, so a future failure reports itself instead of relying on
someone looking.
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>
sorb's call: Gitea on rohana is a push mirror of the canonical git.lab, so a
nightly gitea dump would back up a copy — effort not justified, cron stays off.
Documented the one non-mirror asset for the record: the container registry holds
four images the cluster pulls (incl. threadnet-web and the backup image itself),
which is rebuild time rather than data loss and is covered by #0022/#0033.
For #0030, sorb confirms the age key is also in the password vault, dissolving
the circular dependency found earlier. Noted that the vault is now part of the
restore path and must lead the procedure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cluster-side backups are healthier than assumed: three nightly Borg jobs to a
Hetzner Storage Box, all completing with plausible volumes and working prune
(synapse 199MB/247 files, authentik ~150MB, wikijs 223kB DB-only). No silent
failures.
Critical finding for #0030: the Borg passphrase and SSH key needed to READ those
backups are SOPS-encrypted under a single age key that exists only in the cluster
being backed up and on one laptop — no documented cold copy. Losing both makes all
three repos permanently unreadable. Cold escrow must precede any restore drill.
For #0010 this shrinks the work: the Storage Box + Borg pattern already exists and
is proven, so Gitea needs only its own repo there. The disabled cron (no backups
since 2026-07-30) remains separately urgent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified via DoH: _dmarc.axion1337.de is p=reject (sorb changed it), subdomains
inherit reject with sp= absent per RFC 7489, and the noted DKIM gap was a false
alarm — IONOS uses s1-ionos/s2-ionos/s42582890 selectors, all present with valid
keys. Apex SPF left at ~all deliberately: real mail flows over the apex and DMARC
already enforces reject, so -all adds little while risking silent send breakage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ftp.axion1337.de deleted and verified (NXDOMAIN). All four DNS hygiene issues
from today's batch (#0001, #0003, #0005, plus #0007 earlier) are now closed:
rohana and selendis hardened with Null-MX/SPF -all/DMARC reject, matrix and
www.game removed, ftp removed. Production A/AAAA records untouched throughout.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
matrix.axion1337.de was already removed by sorb independently (platform runs
under .chat), so www.matrix went with it (confirmed NXDOMAIN via two
independent DoH resolvers). www.game deleted through IONOS and verified.
#0005 down to a single remaining item: ftp.axion1337.de.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Previous commit set three issues in-progress, tripping the WIP<=2 rule. The
verification is done; the DNS mutations are sorb's to run in IONOS, so #0001
and #0003 move to waiting (with wartegrund) while #0005 drives the batch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified current axion1337.de zone state via DoH: www.matrix/www.game are
IONOS-default records nothing serves (cluster routes .chat, no matching cert);
rohana is unhardened (no Null-MX/-all/reject) with www.rohana still present;
selendis mail-set untouched; matrix mail-set + autodiscover present; ftp is
IONOS-hosting ballast. Attached a consolidated per-name IONOS action list; the
mutations are sorb's to run in IONOS (no API access from here). Batch in-progress.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cert renewal switched to DNS-01 (IONOS): test issuance validated end-to-end
(LE YR1, valid to 2026-11-12), the shared letsencrypt resolver now renews
rohana/selendis via DNS-01, so the September renewal needs no open port 443.
Syncs the canonical file with the already-closed git.lab tracker issue 7.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Traefik stack is thread-net-git (manual compose deploy on CFGMON). DNS-01 diff
is ready (tlschallenge -> dnschallenge/ionos + IONOS_API_KEY via host .env).
Two human dependencies remain: create the IONOS API key and deploy+verify on
CFGMON (no SSH from here).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consolidate the 13 findings + learnings from the Wiki.js AAR into
docs/wiki/stolpersteine/wikijs.md (config/deploy, theming, navigation,
locale/timezone incl. the standing fork patch, access control, git-storage),
link it from the wiki index, and set the AAR status to harvested.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#0023 (Docusaurus navbar logo) is moot since Wiki.js replaced Docusaurus
(ADR-0014) -> rejected. #0007 (cert renewal, due 2026-09-28) gets a concrete
plan: pursue DNS-01 (approach B, already recommended) before mid-September,
with the port-opening fallback A as a dated calendar checkpoint.
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.
#0024 decided: axionwiki.lab (development-time). #0020 decided: stay on
Docusaurus with Authentik forward-auth, not the last word on the surface. Both
closed with the decision recorded. New follow-ups the user asked to keep:
0046 (move the wiki into the ThreadNet Server Suite, M4) and 0047 (re-examine
surface alternatives beyond BookStack afterwards, M2). STATUS regenerated;
validate, gen_status --check, upstream_drift and pruefe_prosa green.
Externalises what this session held that the migrated repo did not:
- vision/threadnet.md: the three capabilities that justify the forks beyond
rebranding (AV scanning into encrypted rooms, call-quality defaults with a
client-side-only privacy line, expiring guest access via @concierge).
- issue 0043 (M5): case-insensitive uniqueness in the matrix-invitation prompt
stage — the open residual of ADR-0011.
- issue 0044 (M5): auto-restart consumers on SOPS values-secret change — the
footgun behind the on_conflict fix sitting inactive until a manual restart.
- issue 0045 (M1): report_event.admin_message_md unset — content reports
dead-end with no contact path (verified still open against live config).
- sources/protokolle: the raw apo-call diagnosis history, including the four
ruled-out hypotheses and the harmful DB write, as the source behind the AAR.
STATUS.md regenerated (M5 appears for the first time). validate, gen_status
--check, upstream_drift and pruefe_prosa all green in the CI image.
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 5: eight component declarations under docs/components/
(filename = canonical slug, F-008 answered by construction; staged
dormancy of thread-net-git/threadnet-operating finally representable,
game-operating/gameserver as external with their field-test caveats),
five pointer-rollout follow-up issues (0035-0039, ADR-0013),
gruppenpruefung.py joins the stillstandspruefung family (runtime group
list vs declarations, pointer presence, group-wide milestone/priority
duty, issue drift, git hygiene since the 2026-08-07 rule boundary,
bot exception per ADR-0009) with its own scheduled CI job, and
spiegel_issues.py mirrors repo to GitLab (title, state, milestone,
priority, due, status label only - never descriptions, never
backwards, dry-run by default, GitLab-only issues are reported and
never auto-closed).
Verified - all four pattern demos fire (acceptance criterion 5, 4/4):
A) old CLAUDE.md claims M1-M4 while the frozen export knows M5;
B) hygiene over full clone history finds 222 real-clock commits by
own identities (matches the frozen Session-1 numbers per repo);
C) covered in slices 3/4 (five task blocks, now 0);
D) covered in slice 3 (orphaned SHA citations, now resolved/curated).
Live run (read-only): exactly the four missing pointers (F-011) and
gitops#61 without milestone as red findings, zero drift on all 26
mirrored issues, group list consistent. Mirror dry-run plans 7
creations, 0 updates, wrote nothing. Offline chain green: validate
0/0, gen_status --check current, drift 0, prosa 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gate 4, slice 4: 26 open management issues imported from live git.lab
(read-only, descriptions included as authorized; GitLab iid = file id,
labels/milestone/priority/due/host/area mapped into frontmatter, the
import aborts instead of inventing a missing milestone or priority).
Two new issues close the F-004 gap where work was really still open
(0033 OVERMIND-01, 0034 CFGMON-11 incl. the plaintext npm-token
rotation); CFGMON-12/13 already route to verified git.lab issues,
MATRIX-05 is done and needs none (agreed with sorb). The three wiki
task blocks now reference their issues, roadmap.md hands all counts to
the generated STATUS.md and states M1-M5 per ADR-0010 (closing F-001
in the canonical prose), pruefe_prosa joins the CI validate job, and
the import protocol under docs/sources/migration/ records every
intervention into imported text.
Verified: validate 0/0 over 29 issue files, gen_status --check current
(distribution line M1 9 - M2 17 - M4 2 plus per-issue milestone and
priority), pruefe_prosa 0 errors with clones and 0 errors/15 unchecked
citations in offline CI mode, drift check green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>