Files
chemenu/raw/notes/Conversation Transcript - Two Round-Trip Defects Found by an Ingest Session 2026-08-31.md
T
torben 18ae28f918
CI / verify (push) Failing after 32s
Release / release (push) Successful in 38s
Chemenu 2.1.0 - deterministischer Wissenskompiler
Chemenu kompiliert Rohnotizen zu einem verlinkten, quellengebundenen Wiki:
raw/ -> types/ + tools/ -> kb/ -> reports/. Was mechanisch ist, macht
tools/wikitool; was Urteil braucht, macht ein Agent unter Contracts, deren
Grenzen in Code durchgesetzt sind statt im Prompt.

Dieser Commit ist der Startpunkt der oeffentlichen Historie. Die vorherige
Entwicklung fand in einer privaten Instanz statt und ist nicht Teil dieses
Repositorys; ihre Erzaehlung steht vollstaendig in CHANGES.md, das mit 44
Eintraegen von 0.1.0 bis 2.1.0 erhalten geblieben ist.

Der mitgelieferte Korpus ist ein Testbett und eine Demo: 170 Seiten ueber den
Stack selbst - Gates, Lint, Versionierung, Suche, das Wiki-Muster. Er
dokumentiert das Werkzeug mit den eigenen Mitteln des Werkzeugs.

Lizenz: AGPL-3.0 fuer den Stack (tools/, types/), CC-BY-4.0 fuer die Inhalte.
Die Grenze zwischen beiden ist der Dateiplan, den dist export berechnet -
siehe NOTICE.
2026-09-01 16:26:14 +02:00

7.1 KiB

Conversation Transcript - Two Round-Trip Defects Found by an Ingest Session

Source: Claude Code session (claude-opus-5), llm-wiki-test1 workspace Collected: 2026-08-31 Participant: Torben Fidelity: faithful summary transcript, not a verbatim log. Command output and measured counts quoted below are real. The subagent's findings are second-hand relative to this session and were independently verified before being acted on - where that verification changed or extended the claim, the transcript says so. No credentials appeared in the session.

Covers two defects an ingest exposed, both fixed the same day: cite add deleting any content behind the Footnotes block (1.5.1, bb4123b, issue #17) and a source page's reference arrays being unreachable while xref add wrote a field the schema rejects (1.6.0, ce03749, issue #18). Both were prio/1. Both had lived under a fully green test suite.


Turn 1 - the ingest reports two tool defects

A subagent ingesting the touch --set transcript published successfully (8524bce) and then reported two things it had run into. The assistant did not act on the report directly. SOUL.md treats a confidently asserted fact as the cardinal error, and a subagent's finding is second-hand; both claims were reproduced first.

Verification of the first claim - and a hold

split_cite_block() was read directly. The mechanism was exactly as reported: everything from the ## Fußnoten heading to the end of file is taken as the block, only [^id]: lines are kept from it, and every caller reassembles the page as head + rendered block.

A scan over the corpus then measured the exposure rather than estimating it: 8 pages carrying 74 lines in the doomed position, Detect-Repair Asymmetry worst at 14.

The second ingest was held at this point. It would have called cite add on pages in that list. Continuing would have destroyed content to save a few minutes.

Verification of the second claim - and a correction of scope

xref_link_source was read: it writes only the target pages and never the source page's own arrays. Confirmed. types/source.md declares page_ref_fields: [entities, concepts], so the related: that xref add had written was undeclared, and strip_frontmatter_ref() swept only declared fields - so nothing could remove it. One command creating a state another could not undo.

Both were filed as issues (#17, #18) before any code was touched, per instructions/capture-session.md: what is still open belongs in the tracker, not in a transcript or in someone's head.


Turn 2 - "Korrigiere zunächst #17"

The fix, and the option that was rejected

The block now ends at the next heading instead of at end of file. Everything after it - and anything inside it that is not a citation definition - is folded back on to the head.

Two properties turned out to matter more than the repair itself:

  • The page self-heals. Because the rendered block is always emitted last, the first citation operation puts a page that had drifted into the broken layout back in order. xref add may keep appending at end of file without doing harm - the contradiction between the two commands is defused rather than merely avoided.
  • Loose text inside the block is rescued, not rejected. The issue's own third acceptance criterion asked for an abort. That was declined with a reason: the same code path runs under lint and corpus_diff, where raising would refuse to read a page instead of reporting it. Rescuing is strictly better than aborting and satisfies the intent - nothing is discarded silently.

Reading the code added a third affected command the issue had not named: rename uses the same path and would have deleted the same content. And a second loss path with the same cause - a [^id] referenced only in a section behind the block counted as unreferenced, so cite sync would have pruned its definition as an orphan.

The test was proved red before it was trusted

Rather than asserting that the new test would have caught the defect, the old implementation was reconstructed and run against it:

ALTER Code  -> Beziehungen erhalten: False
NEUER Code  -> Beziehungen erhalten: True

Corpus repaired and measured

cite sync --all normalised eleven pages (the eight at risk plus three needing only a re-ordering). Afterwards: 0 pages with content behind the block, and per page an unchanged count of citation definitions and bullets - checked, not assumed. The line-count asymmetry in the diff came from summary: being re-wrapped on to one line, not from lost content.


Turn 3 - "Mache mit 18 weiter"

Three defects, one cause.

xref link-source writes both directions. Which field a target lands in follows its collection: kb/entities/entities:, kb/concepts/concepts:. The directory is the field name, so a new collection needs no code change here - it needs a type declaring the matching field. That was chosen over a type-to-field map precisely because a map is a second copy of something the type-specs already say.

xref add refuses an undeclared related:, checking both pages before writing either, so a refusal cannot leave half a link. The message names the fields the type does declare and the command that fills them.

xref remove sweeps undeclared leftovers, with the field names taken from the type-specs rather than a constant. An undeclared field that ends up empty is dropped outright: the key was never valid for that type, and related: [] would keep the page failing validation.

The repair, done with the tool

No rm --yes, no hand-edited frontmatter. xref remove cleared the leftover, xref link-source recorded both concepts in both directions. lint reports no schema error in the corpus.

The referencing concept page came through the cycle byte-identical - xref remove cleared its back-reference bidirectionally and link-source restored it exactly. That was the real proof that the two commands are inverses.


What the session concluded about itself

The denylist shipped in 1.4.0 that morning was right; its pointer was not. It refused page-reference fields with "use xref add / xref remove" - an unverified claim about another command's capabilities, and false for exactly the fields a source page has. A refusal that routes to another command should ship with a test showing that command covers the case.

Both defects lived under a fully green suite. 678 tests passed before the cite add tests were written; 67 gate tests passed before the counting change earlier the same day. In each case no test had ever asserted the wrong behaviour, which is how it survived. The same finding strengthens the premise of issue #8: a suite verifies what it knows about.


Outcome

Artifact Result
Stack versions 1.5.1 (PATCH), 1.6.0 (MINOR)
Commits bb4123b, ce03749
Tests 689 passing, in the normal and the hardened environment (11 new)
Corpus 8 pages de-risked and normalised; the schema error on main repaired
Gitea #17 and #18 closed