Commit Graph
266 Commits
Author SHA1 Message Date
Thore CimbalandClaude Opus 5 eb55bc4e4b docs: close #0030 — the question in its title was answered a week ago
The subject of the issue was whether the backups were a guess. That was
settled on 14.08. with restored row counts rather than an assurance, and
the title has been wrong ever since. It also sat at in-progress for a week
without work. Third issue today with the same stale-head class.

Two things measured while closing, neither of them in the text:

restore-drill has never fired on schedule. lastScheduleTime is none - the
job was created on 14.08. and runs on the 4th, so the first automatic run
is 04.09. Only the manual run has passed. Every other cronjob in the
cluster shows a fresh timestamp; this one does not. Not a fault, but
"automated" and "proven" are not the same thing.

The Synapse media are backed up and the restore is untested. synapse-backup
mounts the PVC and archives /media/media_store - the run of 21.08. held 320
files and 216 MB, which would not add up without media. The way back is
written down and explicitly not automated, and nobody has walked it.

The homelab item is dropped rather than carried anywhere: the homelab is
not part of this project, and it should not have been listed as remaining
work in the first place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 dc9ed418cf docs: close the measuring ledger
Rows filled after the commit they name exists - the order the judge
insisted on twice yesterday.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 b090733170 docs: #0083 closed by measurement, #0088 corrected to the measured state
Both issues described work that no longer exists. Without measuring, two
tasks would have entered planning half-done.

0083 is done. No single-file mount remains in the compose file; the running
exporter emits both metrics that only exist since the 19.08. change; and
the timing closes the chain - the mount commit carries 12:00Z while
Prometheus and Alertmanager have been up since 16:34Z and Loki since 17:08Z
the same day, so the redeploy came after the change. A "yes, it was rolled
out" would have claimed the same and proven nothing.

0088 stays, with a title that is no longer wrong. It said "13 ingress
rules, 1 egress", which was the state on 06.08.; measured today there are
ten workloads restricted to internal traffic and fifteen still open, not
the eleven the issue listed.

Measuring found more than it corrected. wikijs itself is unrestricted while
its database is, which nobody intended and nothing recorded. And the
namespaces authentik and monitoring carry only the metadata block, so their
egress is entirely open - that is not in the issue at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 48e29621a1 docs: ledger for the brief-filing run
Size S owes no gates, but the ladder section is mandatory even when nothing
was built - an absent record and an ignored rule look identical.

"One ledger per session" does not quite hold here. This session's ledger
was closed at Gate 5 and then this task arrived. A second ledger
contradicts the wording; no ledger contradicts the sentence above it, that
every rule owes a trace or it is decoration. I chose the trace and wrote
the contradiction down rather than resolving it quietly: a session can hold
more than one finished run, and the concept currently knows only sessions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 8280ead37d docs: keep the briefs handed to fresh sessions, in the repo
Three task briefs lived only in a chat and in a home directory. They are
the only record of what was actually handed over, and therefore the only
basis on which a result can later be judged to match its request.

They land beside textbloecke.md rather than in a new directory: that page
already holds "short, copyable blocks you put in front of a session", and
these are the same idea at full length. Each carries its state at the top,
because a brief that reads as pending when it is done is the stale-head
failure this project keeps finding.

The third one exists for a reason worth stating: an agent judging its own
run justifies rather than checks, so that verdict must not be written by
the session that produced the run. And such a brief carries no findings of
the author's own - handing them over buys a confirmation instead of a
check, and then the separation is only cost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 585dd0c849 docs: the closed ledger exposed fifteen findings of my own bookkeeping
Closing the run turned status to closed, and the judge - which checks a
closed ledger strictly and an open one loosely - reported fifteen findings
where it had reported none. All mine, in three classes: no ladder entry
carried the commit it became effective in, gate 4.4 carried no approval,
and the slices stood as gate rows although the template expects them in the
design document at size L ("size M records its slices").

The slice rows are removed, which touches existing rows that "appended,
never rewritten" protects. They should never have been there; the design
document has carried them with their commits all along.

Worth keeping, because it is the whole argument for the ledger: while the
run was open the record looked complete and was not. Only the act of
declaring it finished made the check strict enough to say otherwise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 3ae6a8a2df docs: close the run ledger
Gate 5's row names the commit that closed it, written after that commit
exists - the order the judge insisted on twice today.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 988e4b4e51 docs: Gate 5 closeout — what this work made false, and the design doc to done
Five statements this undertaking contradicts, named rather than left
standing. Two of them are in this document's own earlier gates: the Gate-2
mapping had FB-06 as open when the framework had split the item and closed
that half in v0.1.3, and had FB-09 and FB-11 as open when v0.3.0 covers
them partly. Both only surfaced because the states were checked against the
rule text of the tags instead of the issue status.

The third is FB-12's "point 3 is fixed", whose reasoning - the error is
forgetting, not doing it wrong - was falsified by the same task. That page
is revised rather than appended to, which is the practice this project
harvested and the framework shipped in v0.1.3.

Criterion 5 stays partly met. The deterministic half ran against both of
the day's ledgers without findings; the verdict artifact must not be
written here, because its own template holds that a verdict produced in the
working session is void whatever it says.

Moving the document one level deeper broke three relative links, which
validate.py caught before the commit. Same class as everything else today:
the tool saw what the plan did not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 73afd844a1 docs: complete gate 4.4 in the ledger
The row was written in the same commit as the work, so it could not name
that commit's sha - the second time in this run. The parallel run shows the
order that works: the work commit first, then a commit for the ledger row.
That is what "rows are appended as gates close" means.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 09ed1f7d25 docs: the judge ran, found a real violation of mine, and cannot finish here
Slice 4.4. judge.py caught gate 4.3 naming no commit - the row was written
before the commit existed and the sha never filled in. Classified as model
failure, correctly: the rule is clear, the template says so, I did not do
it. Filled in, then clean. A fabricated sha is refused and fails closed.

Both ledgers of the day pass the deterministic half, including the parallel
run's, which this session did not write.

The verdict artifact is missing on purpose. Its own template says a verdict
produced in the working session is void whatever it says, and an agent
judging its own run justifies rather than checks. Acceptance criterion 5 is
therefore partly met, recorded as partly rather than ticked.

First rule-coverage measurement: 14 of 23 rules observable, nine not -
among them "every line traces to the request", "a check owes proof it can
fail" and "incidents get an AAR". Decoration until they carry a duty to
leave a trace. That is the claim this whole series started from, now a
number instead of a suspicion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 9ca5ec3b6d feat: the twelve findings move to pages with a proven harvest state
Slice 4.3. Every finding is now a stolpersteine page carrying its state,
and the mapping is discharged mechanically: twelve findings in the
register's last hand-maintained revision, twelve pages naming their origin,
zero unassigned.

The states were verified against the rule text of the tags, not derived
from issue status - and that turned up two errors in my own Gate-2 mapping.
FB-06 was listed as open; the framework had actually split the combined
item and closed that half as issue 0028 in v0.1.3, whose WORKFLOW.md
carries "Name what this work made false" verbatim. FB-09 and FB-11 were
listed as open too; v0.3.0 covers them partly through the ledger and the
ladder's trace duty, so they are `partly` with the version named.

Two are `declined`: the framework considered them and will not cover them,
so they remain ours. That state exists because writing `open` for a decided
matter is the failure class this undertaking exists to clear.

The texts are not edited. They came out of git at the register's last
revision and moved unchanged; only repo-relative links were rewritten,
because inline links resolve against the file.

Also done: the duplicated half of the ADR rule is gone from the project
section - permanent exceptions have been upstream since v0.1.2 - and three
descriptions that still called the register an inbox now describe the
pages, the generated overview and the signpost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 fe5286c420 feat: contradict an incomplete hand-reconciliation, not just a forgotten one
Slice 4.2. The receipt duty from ADR-0024 was built yesterday on the
premise that "the error is forgetting, not doing it wrong". The same
upgrade falsified that on the same day: the receipt for schema.yaml was
written, it is truthful, it names correctly what was carried over - and two
fields were lost anyway. Nobody forgot anything.

pruefe_upstream_drift.py now holds our schema.yaml structurally against the
baseline: every type, field, enum value and required entry the baseline
carries must exist here. Adding is allowed, losing is not. Checked
counterfactually against the real loss, which it reports.

For the two other extended files there is no equivalent - they are Python,
and no structural comparison exists. That half stays open and is written
down rather than glossed over.

Building it taught the check two things about itself. It broke the existing
selftest because that fixture wrote schema.yaml as prose - the same
unrealistic fixture FB-12 already records as having made an assertion
worthless for this very script. And my counter-control reported "nothing"
for the removed crash guard, because a crash returns the same exit code as
a failure; only checking for a traceback showed the script never reaches
its summary without it. Both times the control was blunt, not the check.

Sixteen assertions, six deliberate breaks, none uncovered.

The occasion is recorded for the next harvest as two stolpersteine pages -
the first use of the mechanism ADR-0009 decided: a receipt certifies
attention rather than completeness, and a file cannot be authoritative and
immutable at once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 0a9543703d feat: harvest states for recurring patterns, end to end
Slice 1, the tracer bullet: schema, generator and one migrated page, so the
whole chain runs before eleven more depend on it.

Two fields were missing entirely. The v0.3.1 merge did not carry over
wiki-page's status and harvested_in, so the mechanism ADR-0009 decides was
not actually available here. A field-by-field comparison against the
vendored baseline found exactly those two and nothing else - the gap the
previous run predicted when it noted that reconciling the extended files is
a manual step with nothing to contradict it.

The generator gains a clustered section and writes the signpost; collect()
and apply_rules() already existed, so the change is a filter and a fifth
rule rather than a second reader or a new checker.

Running it corrected one of my own design errors immediately. The "without
state" group was written as a warning, and it flagged two perfectly correct
pages: status is optional per ADR-0009 and only meaningful on a page that
tracks a pattern. A permanent complaint with no subject trains people to
ignore the section, so the group is now neutral - with the downside written
into the code, since a pattern that lost its state now looks like an
ordinary page.

Eight controls, seven of them deliberate breaks: both generated files go
stale on a hand edit, an invented state value is refused, the new value is
accepted and clusters correctly, and harvested or declined without a named
version now fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 b179246870 docs: extend the state enum instead of writing a known-wrong state
The owner objected to the plan carrying two findings as "open" when the
framework has considered and declined them, on the grounds that the enum
has no better value. He is right: replacing a state the field cannot
express with a state that is wrong is the exact class this undertaking
exists to clear.

Extending is established practice here rather than an exception - the
extension block in schema.yaml already declares two enum extensions, and
ADR-0024 covers the file. The portability cost is near zero because these
pages never travel upstream: ADR-0008 sends generalized failure classes,
which carry no frontmatter.

The value is `declined`, not `rejected`. Our issue enum already uses
`rejected`, and matching vocabulary would be cheaper, but the meanings
diverge: a rejected issue is finished, while a declined pattern persists
and stays ours to live with. Anyone reading `rejected` would think the page
is disposable.

Measured while deciding where to document it: HERKUNFT.md cannot be
maintained at all. It sits under docs/sources/ and the lock check refuses a
test commit against it, while ADR-0024 declares its table authoritative and
requires it to stay in step with the pair list. A file cannot be both the
maintained truth and immutable. Recorded for the next harvest; an accepted
record is not repaired in passing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 0f161d0c91 docs: Gate 3 — and the accepted ADR that forbids the plan as approved
Option A said FRAMEWORK-BEFUNDE.md would cease to exist as a file. It
cannot: ADR-0024 is accepted, links it twice, and an accepted record is
never edited. Deleting the file breaks validate.py at a line nobody is
allowed to repair.

Resolved without touching the record: the file stays as a generated
signpost of a few lines carrying no inventory of its own. The data live in
the pages, the overview in STATUS.md, and the file only says where both
are. Every reference stays valid and nothing can rot, because a signpost
holds nothing that could. It also means AGENTS.md needs exactly one change
- the duplicated rule - rather than two.

Eleven new pages, not twelve: FB-02 already owns one. collect() already
reads artifacts by directory and type, so the generator gains a filter
rather than a second reader.

The assertions that matter are the ones about absence: a page without a
state must show up as a finding rather than vanish, and harvested without
harvested_in must fail. The difference between "no state set" and "not
there" is the failure class this whole register was started for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 afaa74a6b6 docs: Gate 2 — measured first, and nothing here is duplicated work
The owner's condition on approving Gate 1 was to check whether v0.3.1 is
already adopted before doing anything. It is, as far as the rules go: the
upstream part of AGENTS.md and all of WORKFLOW.md are byte-identical with
the vendored baseline, every new section is present, and the drift check
passes. What is not done is the content: no stolpersteine page carries a
harvest state, nothing generates the findings overview - neither our
gen_status.py nor the upstream one knows the concept - and judge.py is
wired nowhere.

Two ladder results worth keeping. The overview extends gen_status.py rather
than becoming a third generator. And FB-02 already owns its page, linked
from the register twice, so it gets a state instead of a duplicate.

The harvest state is now derivable instead of asserted: the upstream issues
carry it. Three of ours are done there, six open, and two were rejected
with reasons.

That last group exposes a gap in the state space. A pattern the framework
considered and turned down is neither open nor harvested, and the enum has
no third value. Carrying it as open asserts a process that no longer
exists - the very class FB-04 describes. Recorded as a finding for the next
harvest, not invented around here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 acbef0854e docs: Gate 1 for finishing the v0.3.1 adoption — stage 2 only
The undertaking was set up as the whole two-stage adoption and had to be
re-cut on measurement: a parallel run had already completed stage 1 the
same day and recorded it in its ledger. Gate 1 is therefore rewritten
rather than extended - a plan that announces work already done is the same
defect as an issue whose head asserts a diagnosis its appendices refuted.

What is left is the part that fails quietly if the adoption is treated as
finished. The twelve findings still live in a hand-maintained register,
while the framework's ADR-0009 gives them a home with a harvest state.
Four framework versions have shipped since they were handed over, so
"a released version covers this" is measurable for the first time - and
unmeasured. And judge.py is adopted but wired nowhere, with no verdict in
existence.

One decision from 2026-08-20 was deliberately reversed by that parallel
run, with approval and a ladder entry: upstream's check_locked.py was not
adopted because ours does the same job and only had a defect, which was
fixed. Not re-litigated here.

This run keeps a ledger, as now required. It records both the re-cut and a
working-tree contamination during the survey.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-21 12:00:00 +00:00
Thore Cimbal 556d568a63 feat: the drift check now contradicts a forgotten reconciliation
FB-12 point 3: the declared-extended files are deliberately outside the
byte comparison, so nothing reported when upstream changed them. Two of
three were touched between v0.1.1 and v0.3.1 and it only surfaced because
someone thought of it.

Two checks added to the script that already owns the subject, rather than
a second script beside it:

  * completeness — every vendored file belongs to exactly one class.
    check_harvest.py and judge.py were adopted byte-for-byte during the
    upgrade and never entered the pair list; nothing compared them.
  * reconciliation — if upstream touched an extended file between the
    previous and the current baseline, ABGLEICH.tsv of the new baseline
    must acknowledge it.

The acknowledgement lives in its own file on purpose. The first draft
searched HERKUNFT.md's prose for the filename, which is always there in
the inventory table: strict-looking and inert. Its selftest missed that
because the fixture was an unrealistically empty note; the counterfactual
against real history caught it.

Ten assertions, twelve deliberate breaks, none uncovered. Two of the
breaks exposed two useless assertions.
2026-08-21 12:00:00 +00:00
Thore Cimbal a5667741d1 docs: ADR-0024 — the three extended framework files, decided
AGENTS.md requires an ADR for every permanent exception: documenting one
instead of deciding it is an error. schema.yaml, validate.py and
gen_status.py have diverged from the vendored baseline since the
migration on 2026-08-11, described in two places and decided in none.
FB-12 named the gap during the v0.3.1 upgrade.

A prefix comparison was measured and rejected: unlike AGENTS.md, these
extensions are interleaved — component sits between issue and wiki-page,
the two rules sit inside apply_rules, and gen_status replaces the issue
table rather than appending to it.

The honest price is in the consequences: the reconciliation on upgrade
has no machine contradictor. Measured on this very case — upstream
touched two of the three between v0.1.1 and v0.3.1 and nothing reported
it.
2026-08-21 12:00:00 +00:00
Thore Cimbal 1ebbb84ba2 docs: record FB-12 in the ledger, and that it was closed too early 2026-08-21 12:00:00 +00:00
Thore Cimbal b8ca6aa570 docs: FB-12 — the upgrade path was described, never walked
The baseline moved v0.1.1 to v0.3.1 on 2026-08-21, the first real run of
a procedure that had been considered settled since v0.1.2. It failed at
three places: WORKFLOW.md could not be copied byte-for-byte because it
links to the framework's own decision records; pruefe_sperrliste forbade
the upgrade itself, unable to tell an addition under docs/sources from a
change; and the three declared-extended files had to be reconciled by
hand because nothing reports that upstream touched them.

One root cause: the procedure was described and never executed. Two of
the three are fixed, the third is open — which is FB-11 on a new subject.

Our own positive-control rule, not applied: the adoption question was
closed on the description of a procedure. Nobody ran it.
2026-08-21 12:00:00 +00:00
Thore Cimbal a31faf4257 docs: record the lock fix in the run ledger 2026-08-21 12:00:00 +00:00
Thore Cimbal 9be7502678 fix: a new immutable source is an addition, not a change
pruefe_sperrliste read --name-only, so it could not tell a modified
source from a newly added one and flagged both. That made a framework
upgrade impossible by construction: an upgrade must place a new baseline
under docs/sources/upstream/, and the lock rejected exactly that.

Upstream's check_locked.py draws the same line and asserts it ('adding a
new source is not an edit'). Modification and deletion stay locked.
2026-08-21 12:00:00 +00:00
Thore Cimbal 7ec74b4f8a docs: the first run ledger in this repo
The duty arrives with the upgrade it records. judge.py reads it clean.
2026-08-21 12:00:00 +00:00
Thore Cimbal 33a0ee7aec chore: upgrade the neckbeard baseline from v0.1.1 to v0.3.1
Three minor releases at once. Byte-identical surface: only WORKFLOW.md
actually changed (+128/-2); CLAUDE.md and the four templates are
untouched. AGENTS.md takes the new upstream prefix (+22/-2) and keeps our
project section unchanged.

The two declared-extended files were reconciled by hand, because nothing
compares them: validate.py gains upstream's check_vendored_portable, and
schema.yaml gains vendored, project_section_marker, the judged link field
and the ledger and verdict types. gen_status.py needed nothing — upstream
did not touch it. Recorded in the new HERKUNFT.md, including that this
reconciliation has no contradictor and will be forgotten next time.

Newly adopted: check_harvest.py and judge.py, plus docs/ledger/ and
docs/verdict/ with their templates. Deliberately not adopted:
check_locked.py — pruefe_sperrliste.py has done that job here since
2026-08-20, and two tools for one rule is maintenance without gain.

The v0.1.1 baseline stays where it is; docs/sources is immutable.
2026-08-21 12:00:00 +00:00
Thore CimbalandClaude Opus 5 8191d03137 docs: our harvest term list, and the state of the eleven findings
The list of names that must never leave in a harvest lives here rather
than in the framework: the framework must not store the names it exists to
keep out. That placement is the framework's own decision (its ADR-0008),
and this file is our side of it.

Two rules carry the reasoning, both learned expensively. A term the
framework itself uses is shared vocabulary and belongs off the list -
listing it produces noise, and noise teaches people to skip the check. And
a green run proves nothing about absence: a denylist finds only what
somebody thought of, which is exactly the confusion behind the near-leak
of 2026-08-13.

All eleven findings were handed over on 2026-08-20 as nine issues of the
next framework iteration. They stay open here regardless: handed over is
not harvested, and by this file's own rule a finding only counts once the
next iteration actually covers it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-20 12:00:00 +00:00
Thore CimbalandClaude Opus 5 f1114ce743 docs: record sorb's decision on FB-11, keep the finding open
Size class and closing status are carried from now on. That settles the
behavioural half only: it was already the rule before, so what changed is
attention, not mechanism. FB-01 shows how long attention alone holds, so
the finding stays open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-20 12:00:00 +00:00
Thore CimbalandClaude Opus 5 be36131307 docs: record FB-11 — rules without a machine contradictor go unfollowed
sorb asked why the ponytail ladder (AGENTS.md section 1, lines 26-29) is
never used. It is not: it was never named and never demonstrably climbed.

Measuring the whole rule set shows ponytail is only where the pattern
surfaced. Every AGENTS.md rule backed by a script was kept without
exception; every rule without one was broken or ignored:

  - the four mandatory completion statuses appear only in the rule files
    themselves and their baseline copies, in no work product
  - size classes S/M/L likewise, only in rule text
  - WORKFLOW.md demands a design doc for L; docs/design holds exactly one,
    from the migration itself, while the upstream merge, v0.6.0, the
    typecheck gate, the egress lockdown and the game host onboarding ran
    without one

The unguarded rules all sit in the byte-identical baseline, so this repo
cannot add enforcement without breaking pruefe_upstream_drift.py. That is
why this belongs in the harvest list rather than in a new script - and a
script could only count rituals, not judgement (FB-03).

FB-01 suspected the mechanism at one instance and now points here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2Q4Ri8NGwyTZzScvKnWFM
2026-08-20 12:00:00 +00:00
Thore Cimbal 8c96591f4b AGENTS.md: AAR-Pflicht und Befundliste im Projektabschnitt (FB-01)
Entscheidung sorb. Drei Ergaenzungen, alle NACH der Baseline-Marke - die
uebernommene neckbeard-Fassung bleibt byte-treu, pruefe_upstream_drift meldet
0 Fehler:

- Werkzeug-Tabelle um pruefe_sperrliste.py
- neuer Abschnitt 'Nach Deploys und Vorfaellen' mit der AAR-Pflicht
- Verweis auf FRAMEWORK-BEFUNDE.md als Eingangsliste der Framework-Ernte

Die AAR-Pflicht stand bisher nur in roadmap.md. Auf AGENTS.md verweist aber
jedes CLAUDE.md - wer der Zeigerkette folgte, fand sie nicht. Am 2026-08-20
fehlten dadurch sechs AARs auf einmal.

⚠️ Im Text steht ausdruecklich, dass kein Werkzeug diese Pflicht erzwingt.
FB-01 ist damit nur zur Haelfte behoben: die Wissensluecke ist zu, die
Durchsetzungsluecke nicht. Das offen zu benennen ist ehrlicher, als sie mit
einem Zeiger fuer erledigt zu erklaeren.
2026-08-20 12:00:00 +00:00
Thore Cimbal 7e7ac29ecb Sperrliste in der CI: angenommene ADRs und docs/sources sind unveraenderlich
Umsetzung von FRAMEWORK-BEFUNDE FB-10. Anlass: Commit 6652125 hat die
angenommene ADR-0022 veraendert, obwohl AGENTS.md sagt 'binding; never edited,
only superseded'. Der Verstoss wurde gepusht und erst durch Zufall bemerkt.
STATUS.md traegt dasselbe Verbot und wurde nie verletzt - weil gen_status
--check widerspricht. Dieses Skript ist der fehlende Widerspruch.

Erlaubt bleibt genau die Aenderung, die die Vorlage vorschreibt: superseded_by
und status in der abgeloesten ADR setzen. Jede Aenderung am Text ist ein
Verstoss.

Geprueft wird nur der aktuelle Push, nicht die Historie - Altlasten wuerden die
Pipeline dauerhaft rot faerben, ohne dass jemand sie beheben kann (FB-03).

Beide Sperren mit Positivkontrolle belegt: mein realer Verstoss 6652125 wird
rot (23 Zeilen), ein Wegwerf-Commit an docs/sources wird rot, ein sauberer
Commit gruen, ein ungueltiger Bereich rot.

⚠️ Beim Bau ist mir FB-02 prompt wieder unterlaufen: Der erste git-Helfer
verwandelte einen Fehlschlag in einen leeren String - ein ungueltiger Bereich
wurde damit zu 'keine Aenderungen' und bestand stillschweigend. Aufgefallen nur,
weil der Test gegen den Wurzel-Commit lief, der keinen Vorgaenger hat. Jetzt
wirft git() statt zu schweigen, und der Fall ist eine eigene Kontrolle.

GIT_DEPTH 0 fuer den validate-Job: Im flachen Klon faellt die Pruefung auf HEAD~1
zurueck und sagt das - genau messen ist besser als benannt lueckenhaft.
2026-08-20 12:00:00 +00:00
Thore Cimbal 75699c4798 FRAMEWORK-BEFUNDE: drei Befunde zur Arbeitsweise, alle belegt
Von sorb benannt, im Repo nachgeprueft statt bestaetigt oder bestritten:

FB-08 Dokumente werden angelesen, nicht durchgelesen. roadmap.md wurde am
20.08. vollstaendig ausgegeben - 94 Zeilen - und die AAR-Pflicht in Zeile 94
nicht gesehen; sie steht dort seit dem 12.08. Danach wurde dieselbe Datei sogar
bearbeitet. Gleiches Muster: der .npmrc-Kommentar sagt den 404 woertlich voraus
und wurde erst nach dem Fehlschlag gelesen.

FB-09 Gelesene Anweisungen werden nicht befolgt. AGENTS.md Zeile 81 sagt, dass
ADRs nie editiert werden. Die Datei war gelesen.

FB-10 Gesperrte Dateien werden bearbeitet. Commit 6652125 hat die angenommene
ADR-0022 veraendert - committet und gepusht, zurueckgenommen erst mit 6731d2a,
und zwar nicht durch eine Pruefung, sondern durch Zufall beim Oeffnen der
Vorlage.

Die drei sind als Klasse 'Arbeitsweise' gefuehrt, nicht als Verfahrenslueck:
FB-09 ist die gefaehrlichste, weil das Wissen vorhanden war und nicht abgerufen
wurde. Der Vergleich, der es belegt: STATUS.md traegt dasselbe Verbot wie die
ADRs und wurde kein einziges Mal verletzt - weil gen_status --check
widerspricht.
2026-08-20 12:00:00 +00:00
Thore Cimbal 21797a005a FRAMEWORK-BEFUNDE.md: wo das Verfahren nicht getragen hat
Auf Wunsch von sorb: eine Eingangsliste fuer die Framework-Ernte, damit
wiederkehrende Fehlerklassen in der naechsten neckbeard-Iteration nicht erneut
passieren muessen. Je Befund kurze Beschreibung, Ursachen-Einschaetzung, Belege
und ein Vorschlag.

Name bewusst FRAMEWORK-BEFUNDE statt 'Framework Defects': 'Befund' ist im Projekt
der feststehende Begriff (gruppenpruefung meldet Befunde, quittungen quittiert
sie, AGENTS.md: 'Jeder Befund gehoert als Issue erfasst'). 'Defect' waere zudem
zu eng - das Uebergehen der AAR-Pflicht ist kein Defekt DES Frameworks, sondern
eine Abweichung, die eine Luecke darin sichtbar macht. Umbenennen ist ein git mv.

Sieben Erstbefunde, alle aus dieser Sitzung belegt:
FB-01 AAR-Pflicht setzt sich ohne Werkzeug nicht durch
FB-02 'meldet Erfolg, ist aber blind' - sieben Vorkommen, fuenf Werkzeuge
FB-03 Pruefungen erzeugen Befunde, die niemand beheben kann
FB-04 Issues behaupten Zustaende, die laengst ueberholt sind
FB-05 Dashboards und Pruefungen ohne Datenbeleg
FB-06 Dokumente werden ergaenzt, aber nicht revidiert
FB-07 Werkzeuge messen unbemerkt die falsche Instanz

Verlinkt in README (Struktur) und roadmap (Kadenz, bei der Retro) - eine Datei,
die niemand findet, waere selbst ein Befund der Klasse FB-06.

⚠️ validate.py prueft nur unterhalb von docs/. Diese Datei ist damit NICHT
schema-geprueft - bei einer Liste ueber fehlende Durchsetzung ist das eine
Ironie, die im Refinement einen Blick wert ist.
2026-08-20 12:00:00 +00:00
Thore Cimbal 9925f279fd Zwei weitere AARs vom 17./18., und das Muster endlich als Wiki-Seite
sorb hat zu Recht nachgehakt: Meine Aussage, nur der 19. und 20. seien
AAR-pflichtig gewesen, war unbelegt. Die Commits dieser beiden Tage zeigen
zwei weitere Vorfaelle.

1. Safari sendete nach der Freischaltung von v0.5.3 ungefiltert weiter -
   LiveKits sender?.replaceTrack uebersprang den Tausch stumm, das rohe Mikrofon
   blieb auf der Leitung. Datenschutzbezug: Wer den Filter einschaltete, durfte
   annehmen, dass genau das nicht passiert.

2. Alle geplanten Pruefungen waren dauerhaft rot und meldeten damit nichts mehr.

Beide waren in ihren Issues ausfuehrlich dokumentiert - und beide ohne AAR.

Wichtiger als die Nacharbeit: #0054 nennt den Safari-Fall selbst das VIERTE
Vorkommen des Musters 'meldet Erfolg, ist aber blind'. Viermal erkannt, nie als
eigenstaendige Lehre abgelegt - und genau deshalb bin ich am 19./20. dreimal neu
hineingelaufen. Das Muster steht jetzt als Wiki-Seite mit sieben belegten
Vorkommen, Erkennungsmerkmalen und Gegenmitteln.

Der Validator hat die Seite prompt als verwaist gemeldet, bis sie im Index
verlinkt war. Wo ein Werkzeug widerspricht, halte ich den Prozess ein - das ist
exakt der Befund aus #0040.

Nicht als Vorfall gewertet und geprueft: die Boje-Aufraeumarbeit in #0074.
2026-08-20 12:00:00 +00:00
Thore Cimbal 242e839eb6 AARs nachgeholt: vier Pflichtfaelle, die ich uebergangen habe
roadmap.md verlangt einen AAR nach jedem Deploy mit Uebergabe und nach
Incidents. Gestern und heute gab es vier solche Ereignisse - Produktionsausfall
durch rc.2, Verlust der DKIM-Eintraege, Game-Host-Deploy ueber zwei Uebergaben,
volle Wurzelpartition auf rohana mit gerissenem Spiegel. Geschrieben wurde
zunaechst keiner. Der letzte AAR im Repo stammte vom 16.08.

Dokumentiert habe ich stattdessen ausfuehrlich IN den Issues. Das ist nicht
wertlos, aber es ist nicht der Ort, an dem AGENTS.md die Lehren sucht - und die
Folge war messbar: dieselben Fallen wurden an einem Tag mehrfach neu entdeckt.

#0040 um eine Rueckmeldung ans Framework ergaenzt: Die Vorgaben wurden dort
eingehalten, wo ein Werkzeug widerspricht (validate, gen_status, Spiegel), und
uebergangen, wo nur Prosa sie verlangt. Mit Vorschlaegen fuers Refinement -
einschliesslich der ehrlichen Alternative, die Definition von Vorfall enger zu
fassen statt strenger zu kontrollieren.
2026-08-20 12:00:00 +00:00
Thore Cimbal 897755cdef #0088: zehn Arbeitslasten dicht, mit Abnahme
Sperre beisst (wikijs-postgres und element-web-docs erreichen weder Internet noch
CFGMON), erlaubte Pfade funktionieren (DNS, Pod zu Postgres), breite Pods sind
unberuehrt (synapse erreicht rohana, CFGMON und 1.1.1.1).

Zwei eigene Fehlmessungen dokumentiert, weil sie wiederkommen: 10.43.0.1:443 ist
auch aus einem Pod mit breitem Ausgang nicht erreichbar, und /dev/tcp gibt es in
dash nicht - im Synapse-Container meldet es immer Fehlschlag.

Korrektur zur Zaehlung: zehn, nicht neun.

Nebenfund aus sorbs Frage zur URL-Vorschau: Synapses eigene Sperrliste enthaelt
169.254.0.0/16 nicht - heute durch die NetworkPolicy gedeckt, beim Umbau des
Egress-Modells aber nachzutragen.
2026-08-20 12:00:00 +00:00
Thore Cimbal 803488f53b #0088: nein, etwa die Haelfte der Pods braucht kein Internet
Arbeitslasten durchgegangen: neun von zwanzig brauchen gar keinen Ausgang, sechs
genau ein Ziel (Storage Box, rohana), fuenf tatsaechlich breiten Zugang - Synapse
wegen aktiver URL-Vorschau, clamav wegen freshclam, coturn und SFU wegen Medien.

Wichtiger technischer Punkt: NetworkPolicies sind additiv. egress-block-metadata
waehlt ALLE Pods und erlaubt 0.0.0.0/0 - eine zusaetzliche enge Policy fuer
einen einzelnen Pod aendert daran nichts. Der Rueckbau muss das Modell drehen:
default-deny-egress plus ausdrueckliche Erlaubnis je Arbeitslast, so wie es
beim Ingress bereits ist.

Zuordnungen sind aus Konfiguration und Aufgabe abgeleitet, nicht gemessen - das
steht auch so im Issue.
2026-08-20 12:00:00 +00:00
Thore Cimbal 7cda0ee91a #0083 und #0088 nachgemessen und fortgeschrieben
#0083: Die drei im Issue genannten Dateien (prometheus.yml, alerts.yml,
alertmanager.yml) sind laengst Verzeichnis-Mounts - der vorgeschlagene Umbau ist
dort umgesetzt. Offen sind noch fuenf Mounts: loki-config.yaml, config.alloy und
drei .py-Skripte.

Bei den Skripten ist die Falle schlimmer, nicht harmloser: Python liest beim
Start, ein Neustart ist ohnehin noetig - und wird deshalb fuer ausreichend
gehalten. Der Prozess startet neu, der Mount zeigt weiter auf den alten Inode,
der Container meldet Erfolg und fuehrt alten Code aus. Ob die laufende
cve-exporter-Instanz die Aenderung vom 19.08. ausfuehrt, ist ungeprueft.

#0088: Beide Zahlen im Issue sind ueberholt - es sind 15 Ingress-Policies mit 21
Regeln, und eine Egress-Policy existiert inzwischen in drei Namespaces. Sie
sperrt aber genau ein Ziel: den Metadaten-Dienst. Alles andere darf weiterhin
ins Internet.

Der Kern des Befunds gilt damit weiter, nur die Zuspitzung 'unbeschraenkt'
nicht mehr. Beide Issues sagten Dinge, die so nicht mehr stimmen - genau die
Klasse, die heute schon mehrfach Zeit gekostet hat.
2026-08-20 12:00:00 +00:00
Thore Cimbal f266ce912e #0002 abgeschlossen: Game-Host ist angebunden, alle Targets up
Zehn von zehn Targets up, 5 Server mit Klarnamen, Recording Rules liefern die
drei laufenden, Serienzahl von 3615 auf 1336 gefallen.

Die Ursache war nie die Firewall, wie ueber Wochen vermutet: Die Ports waren
gar nicht veroeffentlicht - nur im Docker-Netz erreichbar.

Der Exporter auf 9531 hatte nie funktioniert (Client-API mit Application-Key
ueber http), und das zugehoerige Dashboard war deshalb seit jeher leer, ohne
dass es jemandem auffiel. Beides ersetzt.

Zwei Messfallen festgehalten, weil sie wiederkommen: Der Entwicklungstunnel
fuehrt bei cfgmon.lab:9090 auf eine ANDERE Prometheus-Instanz, der echte
operating-Stack ist nur aus dem Cluster ueber 10.0.0.3 erreichbar. Und
--force-recreate ist fuer Prometheus ueberholt - das Verzeichnis wird gemountet.

Offen und unabhaengig: flux2.json nutzt gotk-Metriken, die es in dieser
Flux-Fassung nicht mehr gibt.
2026-08-20 12:00:00 +00:00
Thore Cimbal ea040649c3 #0103 abgeschlossen: der gruppenlose Zustand ist jetzt erkennbar
Aufraeumteil durch sorb erledigt (wiki-zugang geloescht, die vermutete tote App
gab es nicht). Letztes Abnahmekriterium umgesetzt: taeglicher CronJob zaehlt
Wiki-Nutzer ohne Gruppe und schreibt das Ergebnis nach Loki.

Beide Zweige geprueft - der Warn-Zweig laeuft nur, wenn etwas kaputt ist, also
genau dann, wenn er funktionieren muss.

Nebenfund mit Reichweite ueber dieses Issue hinaus: Die NetworkPolicy-Regeln fuer
einen neu erzeugten Pod sind beim Containerstart noch nicht programmiert. Im
selben Pod gemessen - sofort abgewiesen, nach 20 s erfolgreich. Betrifft
moeglicherweise auch wikijs-backup, synapse-backup und restore-drill.
2026-08-20 12:00:00 +00:00
Thore Cimbal 16b361d972 #0102 abgeschlossen: DMARC gehoert wieder uns und ist scharf
Eigener TXT statt IONOS-CNAME, p=quarantine mit rua; sieben Service-Namen auf
Null-MX und -all; Apex unveraendert. Abnahme erfuellt - Authentik-Testmail kam
nachweislich an, nicht angenommen sondern geprueft.

Berichtsadresse ist gamemaster@axion1337.chat statt eines neuen Postfachs. Das
umgeht den Autorisierungs-Record, an dem eine zonenfremde Adresse still
gescheitert waere: ohne ihn senden die meisten Empfaenger gar nichts, und
ausbleibende Berichte sehen aus wie keine Probleme.

Zwischenfall festgehalten: Bei der Aenderung verschwanden alle drei
DKIM-Selektoren. Kein Ausfall - direkte Zustellung traegt ueber SPF -, aber
weitergeleitete Mail waere bei p=quarantine still im Spam gelandet. Wieder
aktiviert, s1/s2 loesen bis zum Schluessel auf.

Daraus zwei Werkzeug-Lehren, beide umgesetzt (notfallhandbuch 91d7106, b6ee7fd):
pruefe-dns.sh kannte DKIM nicht und haette den Verlust nicht gesehen; und es
misst im Lab den abgefangenen Resolver statt der Zone - eine Anfrage an eine
TEST-NET-Adresse lieferte ein Ergebnis. Beides jetzt abgedeckt, die Gegenprobe
laeuft vor den Messungen.
2026-08-20 12:00:00 +00:00
Thore Cimbal 9e01e665ef #0002: Ursache belegt, zentrale Seite umgestellt
docker ps zeigt es eindeutig: Die Exporter veroeffentlichen ihre Ports gar nicht,
sind also nur im Docker-Netz erreichbar. Weder Firewall noch Bind-Adresse.

Zentrale Seite auf 10.0.0.4 umgestellt und der seit drei Monaten laufende
pterodactyl_exporter erstmals angebunden - das vorhandene Dashboard nutzt genau
dessen Metriken und war deshalb dauerhaft leer.

Host-Seite mit Reihenfolge und Stolpersteinen dokumentiert; Compose uebergeben.
2026-08-20 12:00:00 +00:00
Thore Cimbal 8e64615b76 #0077 erledigt, #0002 vermessen
#0077: ClamAV-Dashboard liegt in threadnet-operating (7ee42f9). Zwei Korrekturen
am Issue-Text, beide gemessen: das vorgeschlagene Label app existiert in dieser
Loki nicht (Cluster-Logs haengen an job=loki.source.kubernetes.k8s_logs mit
instance=<ns>/<pod>:<container>), und der Text kennt nur das Synapse-Modul -
die wichtigere Quelle ist der Client-Scanner, der auch verschluesselte Raeume
abdeckt. In sieben Tagen: 7 Treffer beim Client, 0 beim Modul.

Zusaetzlich ein Panel, das im Issue nicht stand: Fail-open-Faelle. Laesst der
Scanner mangels clamd Dateien durch, ist das sonst unsichtbar.

#0002: Der Host liegt inzwischen im vSwitch unter 10.0.0.4 - Ports 22/80/443
offen, 9100 und 8080 zu. Eine Umstellung der Targets auf die private IP genuegt
also NICHT; die Exporter binden nicht auf der privaten Schnittstelle. Der noetige
Handgriff liegt auf dem Game-Host. Mit Kontrollmessungen belegt, damit ein
stiller Fehlschlag nicht wie ein Befund aussieht.
2026-08-20 12:00:00 +00:00
Thore Cimbal ed87ffe236 STATUS nach Meilenstein gruppieren, #0032 abgelehnt
#0032 (gameserver ohne Push-Mirror) auf rejected: Der Game-Server ist nicht Teil
des Arbeitsumfangs. Der Befund bleibt sachlich richtig, er ist nur nicht unserer.

STATUS.md listete alle offenen Issues flach; die Frage "was gehoert zu M1" war
darin nur durch Suchen zu beantworten. Jetzt nach Meilenstein gruppiert und
innerhalb dessen nach Prioritaet - ersetzend, nicht ergaenzend: derselbe Bestand
zweimal untereinander waere genau die Doppelpflege, die diese Datei vermeidet.

roadmap.md bekommt die Bedeutung der fuenf Meilensteine in einem Satz je Zeile.
Die stand bisher nur in den GitLab-Milestone-Beschreibungen und war im Repo
nirgends nachlesbar. Zahlen bleiben draussen - die Datei sagt in ihrem eigenen
Kopf, warum (F-001/F-010).
2026-08-20 12:00:00 +00:00
Thore Cimbal 6c5ecd4fef #0099: Tor deckt den ganzen Baum ab, Spiegel ist wieder gleichauf
Zwei Folgen des Merges, die im Issue noch als offen bzw. enger beschrieben waren.

Das Tor war am 19.08. bewusst auf apps/web beschraenkt, weil pnpm -r lint:types
an apps/desktop/electron-builder.ts abbrach und apps/web nie erreichte. Der
Fehler ist behoben (ThreadNet-Web:ac343e9), der Job prueft jetzt pnpm -r, und
desktop_linux/desktop_windows fuehren ihn ebenfalls als needs.

Der Gitea-Spiegel steht wieder auf 8ca03fe. ADR-0023 fuehrt ihn in den
Konsequenzen als ungeloest - die ADR bleibt unangetastet, eine angenommene ADR
wird nicht editiert; die Aufloesung steht hier.

Zwei meiner Diagnosen dazu waren falsch und stehen als solche im Text: weder
Paketgroesse noch ein Zeitlimit, sondern der Zustand des Ziel-Repos (9,6 GB
gegen 649 MB lokal) bei gleichzeitig voller Wurzelpartition.
2026-08-20 12:00:00 +00:00
Thore Cimbal beeeaed145 #0055: Korrektur - "vollstaendig geschlossen" stimmte nicht
v0.6.0-rc.1 scheiterte am 2026-08-19 mit genau dem 404, den dieses Issue behoben
haben sollte. Die .npmrc stand nie in der COPY-Zeile von apps/web/Dockerfile und
kam im Docker-Build nie an.

Die Abnahme hat gemessen, ob die Konfiguration wirkt, wenn sie vorhanden ist -
nicht, ob sie ueberall ankommt, wo installiert wird. Gutgegangen ist es nur,
weil --frozen-lockfile unter pnpm 10 die gepinnte Tarball-URL nahm und gar nichts
aufloeste; der Schutz kam vom Lockfile, nicht von der .npmrc. pnpm 11 prueft mit
minimumReleaseAgeStrict jeden Eintrag, loest wieder auf und landet ohne .npmrc
bei npmjs. Genau diesen Ablauf sagt die .npmrc in ihrem eigenen Kommentar voraus.

Behoben in ThreadNet-Web:c90ac4d. Status bleibt done - korrigiert wurde die
Behauptung, nicht der Zustand.

Lehre: Fuer Konfiguration, die ueber ihre Position im Dateisystem wirkt, ist die
Liste der Orte die Pruefung, nicht das Verhalten an einem davon.
2026-08-20 12:00:00 +00:00
Thore Cimbal 6731d2a70f ADR-0023: fremde Historie erklaert von der Git-Hygiene ausnehmen
Der Upstream-Merge holte 70.265 fremde Commits nach ThreadNet-Web; 39 davon
faerbten die Gruppenpruefung als Echtzeit-Stempel rot. Sie verletzen die
Konvention wirklich, konnten ihr aber nie folgen und werden sich nie aendern -
also ein Dauerrot, und ein Dauerrot meldet nichts mehr (#0104).

Neues Feld fremdhistorie in docs/components/*.md. Wo gesetzt, prueft die
Hygiene nur Commits, die von eigenen Identitaeten COMMITTET wurden. Der
Trennschnitt ist gemessen, nicht geraten: 18 eigene Commits, alle von uns
committet; 39 fremde von GitHub/RiotRobot; keine Ueberschneidung. Der Autor
taugt nicht - eigene Commits koennen fremde Autoren tragen (Cherry-Picks).

Der Feldwert ist die Begruendung, kein Schalter - Muster der Quittungen aus
ADR-0020. Die Ausnahme gilt nur, wo sie deklariert ist, nicht global; der Preis
(ein Commit unter voellig unbekannter Identitaet faellt dort durchs Raster)
steht in den Konsequenzen.

Belegt: nach dem Fix 0 offene Befunde bei 20 Quittungen, und in ThreadNet-Web
werden weiterhin 18 Commits geprueft, alle auf 12:00:00.

Ausserdem zurueckgenommen: mein Nachtrag an ADR-0022. Eine angenommene ADR wird
nicht editiert (Regel in der Vorlage) - die Erkenntnis steht jetzt in #0099.
2026-08-19 12:00:00 +00:00
Thore Cimbal 6652125ed3 #0099 geschlossen: Upstream-Anschluss vollzogen, v0.6.0 in Produktion
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.
2026-08-19 12:00:00 +00:00
Thore Cimbal bae97e6ca6 docs(adr): ADR-0022 for the upstream reconnection, corrected by the test
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.
2026-08-19 12:00:00 +00:00
Thore Cimbal 1874ddfa92 docs(issues): #0099 step 3 - the graft holds and turns twelve patches into four
With the full upstream history the common ancestor could be searched rather than
guessed: measuring tree distance, not dates, over every develop commit in the import
window. deadd548 from 2026-05-08 wins with 43 differing files against 783 for the tag
we wrongly assumed. Thirty-one of those are mode-only, the historical import bug, and
the content remainder is precisely our own Discord feature plus LICENSE and CHANGELOG.

Tested in a throwaway clone rather than asserted: without the graft there is no
merge-base, with it deadd548 becomes one, and merging three months of develop yields
32 conflicting files. Only four are source, and they are exactly our patches - the
rest are GitHub workflows we do not use and the lockfile.

One intermediate result was worthless and nearly passed as success: merging v1.12.26
reported zero conflicts while not running at all, because the tag was fetched shallow
and carries a single commit. Checking MERGE_HEAD is what caught it.

The real gain is not the number. The issue warns that if Element moves a viewmodels
file during the MVVM rework, our lines vanish without a conflict and git says nothing.
With a genuine ancestor, git says something.

Adopting this is ADR-worthy either way - a shared replace ref or a one-time unrelated-
histories merge - so the decision stays with sorb.
2026-08-19 12:00:00 +00:00
Thore Cimbal 8c07c365f6 docs(issues): #0099 step 2 - the remote is in place and it contradicts the premise
upstream is configured and both tags are fetched, so updates can finally be looked at
as a diff. The first diff disproved what this issue and the fork doc have both said
since August: the import is not the v1.12.17 tag. 783 files differ, 115 exist only on
our side, and two of them date the tree - TextualBodyViewModel.tsx and
RovingTabIndex.tsx are absent from the tag, present in v1.12.26 and present in our
import. The changelog stops at 1.12.17 only because it is written at release time.

So the base is a develop state after the 1.12.17 release. Fittingly it is the MVVM
files that prove it - the area this issue already warned about.

Step 3 changes accordingly: grafting onto v1.12.17 would put every future merge on an
invented ancestry, and worse than raising false conflicts, it could hide real ones.
Finding the actual ancestor means searching develop history, which needs the full
upstream repository rather than a shallow tag fetch.

The advisory assessment is unchanged - the base sits below 1.12.22 either way.
2026-08-19 12:00:00 +00:00