18ae28f918
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.
208 lines
11 KiB
Markdown
208 lines
11 KiB
Markdown
# Conversation Transcript - ENVIRONMENT.md as an Optional Third Session-Level File 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.** Torben's instructions and
|
|
> decisions are quoted verbatim; issue text, file excerpts, tool output and the Mass-Update
|
|
> Gate's printout are real, taken from the session; the assistant's reasoning is condensed.
|
|
> No second-hand material - no subagent was used. No credentials appeared; the Gitea MCP calls
|
|
> used the session's own configured access, not a token pasted into chat.
|
|
> One of two transcripts cut from this session; the other covers coverage reporting in CI
|
|
> (Gitea #10) and the `dist export` leak it uncovered - bundled into the same publish, but a
|
|
> different subject.
|
|
|
|
Covers Gitea issue #24: a third root document alongside `USER.md` and `SOUL.md`, recording what
|
|
*this checkout* works through - harness, published skills, MCP servers, connectors, git remotes,
|
|
where CI runs. Shipped as stack **1.8.0** in commit `a243a4a`, issue closed. The session opened
|
|
with a mismatch between the issue number Torben named and the work he described, and resolving
|
|
that by asking is the first turn.
|
|
|
|
---
|
|
|
|
## Turn 1 - the issue number did not match the task
|
|
|
|
Torben's instruction, verbatim:
|
|
|
|
> `/stack-dev implementiert #10`
|
|
>
|
|
> "Implementiere eine kleine Erweiterung im dev skillset: eine checkout spezifische Datei soll
|
|
> verzeichnen, über welche skills/connectoren/mcp-server/remotes gearbeitet wird. Das sind für
|
|
> Entwicklungen tendenziell konstante Werte, du musst sie aber immer wieder abfragen. Das wäre
|
|
> also Ein File, was beim bootstrapping vom jeweiligen harness (hier: Claude Code) verlinkt
|
|
> wird. Es soll optional sein."
|
|
|
|
The assistant read #10 first. Its title is **"Coverage-Reporting: erst messen, dann eine
|
|
Schwelle setzen"** - pytest-cov in CI, no threshold until the number is known. Nothing to do
|
|
with a checkout-specific environment file. A scan of all twelve open issues found no issue
|
|
matching the description either.
|
|
|
|
**Decision: ask rather than guess.** Two readings led to materially different work, and both
|
|
were plausible - a transposed digit, or two requests in one message. The alternative considered
|
|
and rejected was implementing the described feature and treating "#10" as a slip, which would
|
|
have silently dropped a work package Torben may have meant.
|
|
|
|
Torben's answers to the three questions:
|
|
|
|
- Scope: **"Beides in dieser Session"** - both work packages, not one.
|
|
- Placement: **"Root, für jede Instanz"** - not dev-only.
|
|
- Name: **"ENVIRONMENT.md"**.
|
|
|
|
The described feature had no issue, so one was filed as **#24** (`prio/2`, `size/M`) before the
|
|
changelog entry was written, so the entry could name a number that already existed.
|
|
|
|
## Turn 2 - why not `instructions/dev/`
|
|
|
|
Torben's phrasing was "eine kleine Erweiterung im dev skillset", which points at
|
|
`instructions/dev/`. The assistant surfaced a hard constraint from
|
|
`instructions/CONTRACT.md` before offering the choice:
|
|
|
|
> `tools/wikitool instructions verify` also reports anything under it that is referenced from
|
|
> **outside** it, because such a reference would dangle the moment `dist export` runs.
|
|
|
|
So a `CLAUDE.md` link into `instructions/dev/` is a boundary violation unless it is wrapped in
|
|
`<!-- dist:strip-start/end -->` markers - the one sanctioned crossing, used today by exactly one
|
|
routing line in `AGENTS.md`. That is more mechanism for less reach.
|
|
|
|
**Rejected: dev-only placement.** The content - which MCP server answers which question, which
|
|
remote `publish` talks to - is not development-specific. A distributed instance running content
|
|
sessions has the same questions. Torben chose root.
|
|
|
|
## Turn 3 - the three properties that separate it from the Personalization Plane
|
|
|
|
The existing pattern (`kb/concepts/Personalization Plane.md`, stack 1.1.0) is: ship a
|
|
`.template`, fill it during setup, check it with `doctor`. `ENVIRONMENT.md` reuses the shape and
|
|
diverges on three points, each deliberate.
|
|
|
|
**Optional, and `doctor` never FAILs.** `check_environment()` returns `OK` when the file is
|
|
absent, `OK` when filled, and `WARN` only for a renamed-but-unfilled template. Quoting the
|
|
docstring written for it:
|
|
|
|
> Missing it costs a session some questions, not correctness, so this check never FAILs - the
|
|
> whole point of the file is that it is optional, and a FAIL would make it mandatory by the
|
|
> back door.
|
|
|
|
**Rejected: `FAIL` on missing**, which is what `personalization` does. That check is right for
|
|
`USER.md`/`SOUL.md`, which are an operating requirement; here it would have converted "optional"
|
|
into "mandatory with a nicer word".
|
|
|
|
The one case still worth reporting is the failure mode a plain existence check misses: a file
|
|
that is present, loaded into every session, and answers nothing. Same sentinel as the
|
|
personalization pair, `wikitool:template-unfilled`.
|
|
|
|
**Gitignored, not committed.** `USER.md`/`SOUL.md` are committed here and excluded from
|
|
`dist export` by the root allowlist. `ENVIRONMENT.md` goes further and is gitignored, on the
|
|
argument that two clones of one repo are two different environments - a committed copy would
|
|
hand the second clone answers that are *wrong* rather than missing, and wrong is worse here
|
|
because the file gets believed.
|
|
|
|
That created a trap worth pinning: the careless pattern `ENVIRONMENT.md*` swallows the template
|
|
too. `docs verify` now asserts both halves - `ENVIRONMENT.md` in `REQUIRED_IGNORE_CANARIES`,
|
|
`ENVIRONMENT.md.template` in `REQUIRED_TRACKED_PATHS`. The `.gitignore` entry is anchored
|
|
(`/ENVIRONMENT.md`) per that file's own header rule.
|
|
|
|
**Context, not authority.** Written into the template, `AGENTS.md` and the file itself: it
|
|
describes what is *there*, not what is permitted. A remote listed in it does not authorize a
|
|
`git push` - invariant 5 still routes through `wikitool publish` - an MCP server listed in it
|
|
opens no gate, and nothing in it is a source under invariant 3. No credentials: it sits in
|
|
plaintext in the working tree and in every agent's context.
|
|
|
|
## Turn 4 - import or link in CLAUDE.md
|
|
|
|
`instructions/CONTRACT.md` frames this as a question about *when the decision is made*: an
|
|
import for what is decided in passing, a link for what is looked up deliberately. An import is
|
|
the strongest and most expensive load in the layer, charged to every session on that harness.
|
|
|
|
**Decision: `@ENVIRONMENT.md`, an import.** The reasoning written into `CLAUDE.md`:
|
|
|
|
> a session that has to go look the answer up will instead ask the user again, which is the
|
|
> cost the file exists to remove.
|
|
|
|
The file is also the first import that may legitimately never exist. `CLAUDE.md` already
|
|
tolerates unresolved imports - `USER.md`/`SOUL.md` do not exist during the setup session - so
|
|
the tolerance was already proven; here it becomes a permanent state rather than a transitional
|
|
one.
|
|
|
|
Two knock-on edits the change forced: `CLAUDE.md`'s closing paragraph said "The fourth import is
|
|
the harness-specific one", which the new import made wrong, so both ordinals became "the last
|
|
import". `AGENTS.md` got a file-naming row *and* a separate `## Environment` section, because
|
|
the other harnesses read `AGENTS.md` and never see `CLAUDE.md`.
|
|
|
|
**Rejected: a `wikitool environment` command** to scaffold or query the file. It is read often
|
|
and written rarely; a command for it would be machinery with no consumer. The template plus two
|
|
instruction steps is the whole surface.
|
|
|
|
## Turn 5 - where the file gets created
|
|
|
|
Two entry points, both optional, both with the same rule:
|
|
|
|
- `instructions/bootstrap.md` step 5 (new), for a clone - which never has the file, because it
|
|
is gitignored.
|
|
- `instructions/setup-instance.md` step 9 (new), for a fresh distribution.
|
|
|
|
Inserting a step into `setup-instance.md` meant renumbering 9-13 to 10-14, and one cross-
|
|
reference inside step 6 pointed at the `doctor` step by number ("Schritt 11") and had to move to
|
|
12. Three other documents reference "Schritt 6 (Personalization)" - `INSTALL.md` twice,
|
|
`bootstrap.md` once, `CHANGES.md` three times - and step 6 did not move, so those stayed correct.
|
|
|
|
The rule written into both steps, and into the template:
|
|
|
|
> eintragen was ablesbar ist, einmal fragen, "weiß ich nicht" akzeptieren, einen leeren
|
|
> Abschnitt löschen statt ihn mit Plausiblem zu füllen
|
|
|
|
A guessed line is worse than a missing one, for the same reason the file is gitignored.
|
|
|
|
## Turn 6 - human docs, then publish
|
|
|
|
Per the `stack-dev` skill's rule that a stack change is unfinished until the human docs describe
|
|
it: `README.md`'s architecture tree gained a row and its `CLAUDE.md` line was corrected;
|
|
`INSTALL.md` gained a paragraph in "Weg C", a line in the `doctor` summary and a troubleshooting
|
|
entry for `environment: WARN`; `tools/CONTRACT.md`'s `doctor` row names the new check.
|
|
|
|
Verification before publishing: 730 tests, `docs verify`, `instructions verify`,
|
|
`lint --fail-on-error`, and a full replay of the CI smoke test - `dist export` into a scratch
|
|
directory, `git init`, stub personalization, venv, `instructions sync`, `index rebuild`,
|
|
`sources rebuild-index`, then `doctor`/`docs verify`/`instructions verify`/`lint`/
|
|
`migrate status`. `doctor` reported `environment: ENVIRONMENT.md absent (optional)` there, which
|
|
is the intended answer for a fresh instance.
|
|
|
|
`publish` exited **42** at the Mass-Update Gate (23 files, threshold 10). Per invariant 6 the
|
|
full breakdown was reproduced to Torben and the session stopped. His clearance, verbatim:
|
|
|
|
> "Wenn wir im Rahmen der ci scripts nichts mehr brauchen ist der aktuelle publish freigegeben"
|
|
|
|
The assistant checked `nightly.yml` and `release.yml` first - neither runs pytest, and the new
|
|
`doctor` check can never FAIL, so neither needed anything - then published with the token.
|
|
|
|
## Turn 7 - the file for this checkout
|
|
|
|
Torben, verbatim:
|
|
|
|
> "Erstelle eine Environment.md für diese Session. Sei kurz und prägnant, die meisten Infos
|
|
> solltest du ja haben."
|
|
|
|
A first version had carried the template's full preamble. Rewritten to 40 lines holding only
|
|
values: Claude Code primary with Codex/Copilot/Vibe alongside, six published skills including
|
|
`stack-dev`, the `gitea-mcp` server (with the standing rule that Gitea is read through it and
|
|
not through curl), `origin` at `ssh://git@gitea.nehmer.net:10101/torben/llm-wiki-test1.git` with
|
|
the private-repo token note, CI on Gitea Actions with its three workflows, plus two standing
|
|
facts - this repo is a testbed rather than the production instance, and contracts/instructions
|
|
are English while `kb/` content and changelog entries are German.
|
|
|
|
`doctor` then reported `environment: ENVIRONMENT.md present and filled`, and
|
|
`git check-ignore -v` confirmed `.gitignore:111:/ENVIRONMENT.md` keeps it out of every commit.
|
|
|
|
---
|
|
|
|
## Outcome
|
|
|
|
| | |
|
|
|---|---|
|
|
| Version | 1.8.0 (`--minor`: new capability, backwards compatible) |
|
|
| Commit | `a243a4a` (shared with the coverage work, 23 files, +589/-24) |
|
|
| Issue | #24 filed and closed in this session |
|
|
| Tests | 730 passing; new tests for three `doctor` outcomes, the `dist export` split, both ignore-canary halves |
|
|
| Verified | `docs verify`, `instructions verify`, `lint --fail-on-error`, full `setup-instance` replay against a fresh export |
|
|
| CI | Run 87 green, all nine steps; release run 88 green |
|
|
| Gate | Mass-Update Gate, exit 42, cleared by Torben after the file list was shown |
|