incoming/.gitkeep als Datei- statt Verzeichnismuster trackbar (schliesst #88)
Files changed: - .gitignore - CHANGES.md - README.md - VERSION - incoming/.gitkeep - instructions/bootstrap.md - raw/CONTRACT.md - tools/CONTRACT.md - tools/chemenu/commands/docs_verify.py
This commit is contained in:
+19
-8
@@ -155,19 +155,30 @@ npm-debug.log*
|
||||
# one is merely tolerated and ignored; `wikitool raw accept` promotes it into
|
||||
# `raw/`, computing the date-sharded directory and any bundle from what was
|
||||
# passed in one call. Unlike raw/ itself this must NEVER be committed - the
|
||||
# promotion is what makes a file immutable, not the drop - so this is the one
|
||||
# directory pattern in this file that is deliberately not anchored back open
|
||||
# by the content backstop below.
|
||||
/incoming/
|
||||
# promotion is what makes a file immutable, not the drop.
|
||||
#
|
||||
# File pattern, not directory pattern (Gitea #88): `/incoming/` used to
|
||||
# exclude the directory itself, and rule 2 above means the negation block at
|
||||
# the bottom could never re-include a file whose parent was already gone -
|
||||
# so a fresh clone never had the directory at all, only
|
||||
# `instructions/bootstrap.md` recreating it by hand. `/incoming/*` excludes
|
||||
# everything inside instead, so `!/incoming/.gitkeep` right below actually
|
||||
# applies: that one anchor file is trackable and ships with every clone.
|
||||
# Nothing else dropped here is rescued by the same rule.
|
||||
/incoming/*
|
||||
!/incoming/.gitkeep
|
||||
|
||||
# MCP `submit` tool quarantine (see raw/CONTRACT.md "Getting a file in from
|
||||
# outside" and tools/chemenu/upload.py). Material pushed by a caller that is
|
||||
# not this terminal, before a human has reviewed any of it - stronger than
|
||||
# `incoming/` above: not merely uncommitted, but read by no command in the
|
||||
# ordinary pipeline. No `.gitkeep`: the directory is created on demand by
|
||||
# the one function that is allowed to write into it, and a checkout that
|
||||
# never arms the `submit` tool never gets one. Never anchored back open by
|
||||
# the content backstop below, same as `incoming/`.
|
||||
# ordinary pipeline. Unlike `incoming/` above (Gitea #88), this one keeps the
|
||||
# directory form and gets no `.gitkeep`: the directory is created on demand
|
||||
# by the one function that is allowed to write into it, and a checkout that
|
||||
# never arms the `submit` tool never gets one - there is no fresh-clone case
|
||||
# to cover here, since nothing reads this path before that function creates
|
||||
# it. Never anchored back open by the content backstop below, same as
|
||||
# `incoming/`.
|
||||
/mcp-upload/
|
||||
|
||||
# Content backstop - keep this block last. Nothing under raw/, kb/ or work/ may
|
||||
|
||||
+37
-1
@@ -35,7 +35,7 @@ dev-checkout concern - readable here, never shipped as something to parse.
|
||||
|
||||
---
|
||||
|
||||
## 5.0.0-beta.14 - 2026-09-11 - tools/CONTRACT.md als Nachschlage-Dokument strukturiert: ###-Gruppen in beiden Tabellen, spiegelnde Reihenfolge, Lead-in-Regel
|
||||
## 5.0.0-beta.15 - 2026-09-11 - incoming/.gitkeep als Datei- statt Verzeichnismuster trackbar (schliesst #88)
|
||||
|
||||
**Author:** Torben Nehmer
|
||||
|
||||
@@ -67,6 +67,7 @@ dev-checkout concern - readable here, never shipped as something to parse.
|
||||
- AGENTS.md-Changelog-Absatz korrigiert: Contract-Prosa ist Sitzungsarbeit, doc-pull-through-Instruction ergaenzt
|
||||
- docs verify prueft die Kommando- und Fehlerkontrakttabelle in tools/CONTRACT.md getrennt, 10 fehlende Fehlerkontrakt-Zeilen nachgetragen (schliesst #91)
|
||||
- tools/CONTRACT.md als Nachschlage-Dokument strukturiert: ###-Gruppen in beiden Tabellen, spiegelnde Reihenfolge, Lead-in-Regel
|
||||
- incoming/.gitkeep als Datei- statt Verzeichnismuster trackbar (schliesst #88)
|
||||
<!-- /wikitool:bumps -->
|
||||
|
||||
|
||||
@@ -1308,6 +1309,41 @@ Verifiziert: `tools/wikitool docs verify`, `tools/wikitool instructions verify`,
|
||||
`pytest`-Suite (1195 passed). Typischer Zugriff: Median 229 statt 17.500 Tokens.
|
||||
Schließt #92.
|
||||
|
||||
**`incoming/.gitkeep` ist jetzt trackbar - Datei- statt Verzeichnismuster in `.gitignore` (#88).**
|
||||
`/incoming/` schloss bisher das Verzeichnis selbst aus, und Regel 2 im `.gitignore`-Kopf sagt
|
||||
genau, warum das eine Falle ist: git kann keine Datei wieder einschließen, deren Elternverzeichnis
|
||||
ausgeschlossen ist, also blieb ein `!incoming/.gitkeep` wirkungslos. Ein frischer Klon hatte den
|
||||
Eingang deshalb nie - `instructions/bootstrap.md` Schritt 2 (`mkdir -p incoming`) fing das für
|
||||
einen Menschen ab, der die Instruction liest, aber nicht für einen Checkout, den eine Maschine
|
||||
anlegt: der Korpus-Checkout des MCP-Read-Servers entsteht aus Klon plus `fetch && reset --hard`
|
||||
und läuft durch keinen Bootstrap, also fehlte der Eingang dort still, bis das erste Kommando ihn
|
||||
brauchte.
|
||||
|
||||
Umgestellt auf `/incoming/*` plus `!/incoming/.gitkeep` - jetzt ignoriert das Muster den Inhalt,
|
||||
nicht das Verzeichnis, und die Negation greift tatsächlich. Gegengeprüft in beide Richtungen:
|
||||
`git check-ignore --no-index incoming/.gitkeep` meldet die Datei als nicht ignoriert,
|
||||
`incoming/probe.pdf` weiterhin als ignoriert - die Kanarie aus
|
||||
`docs_verify.REQUIRED_IGNORE_CANARIES` hält also unverändert. `incoming/.gitkeep` steht jetzt
|
||||
zusätzlich in `REQUIRED_TRACKED_PATHS`, damit eine künftige Rückkehr zur Verzeichnisform
|
||||
`docs verify` laut fehlschlagen lässt statt still zu wirken. Ein voller `dist export` +
|
||||
`git init` + `git add -A`-Replay bestätigt, dass `incoming/.gitkeep` im allerersten Commit einer
|
||||
frischen Instanz landet, während `incoming/probe.pdf` dort weiterhin ignoriert bleibt.
|
||||
|
||||
`instructions/bootstrap.md` Schritt 2 entfällt ersatzlos - ein frischer Klon braucht ihn nicht
|
||||
mehr -, die übrigen Schritte rücken nach. `mcp-upload/` bleibt bewusst bei der Verzeichnisform
|
||||
ohne `.gitkeep`: die Schreibprimitive des `submit`-Tools legt das Verzeichnis selbst an, sobald
|
||||
sie gebraucht wird, und ein Checkout, der das Tool nie aktiviert, braucht das Verzeichnis auch
|
||||
nie - anders als bei `incoming/` gibt es dort keinen Frisch-Klon-Fall abzudecken. Der
|
||||
`.gitignore`-Kommentar zu `mcp-upload/` benennt diesen Kontrast jetzt ausdrücklich, statt sich
|
||||
nur noch mit dem alten (identischen) Verhalten von `incoming/` zu vergleichen.
|
||||
|
||||
Geändert: `.gitignore` (beide Kommentarblöcke, neues Muster), `incoming/.gitkeep` (neu),
|
||||
`tools/chemenu/commands/docs_verify.py` (`REQUIRED_TRACKED_PATHS`), `instructions/bootstrap.md`
|
||||
(Schritt 2 entfernt, Rest umnummeriert), `raw/CONTRACT.md`, `tools/CONTRACT.md` (`dist export`-
|
||||
Zeile), `README.md` (Architekturdiagramm-Kommentar). Verifiziert: `tools/wikitool docs verify`,
|
||||
`tools/wikitool instructions verify`, volle `pytest`-Suite (1195 passed), manueller
|
||||
`dist export`-Replay. Schließt #88.
|
||||
|
||||
---
|
||||
|
||||
## 4.7.4 - 2026-09-04 - bootstrap.md nennt den session-id-WARN nach frischem Bootstrap explizit als erwartet
|
||||
|
||||
@@ -79,7 +79,7 @@ chemenu/
|
||||
├── mcp-upload/ # QUARANTINE (optional): the MCP `submit` tool's write path, gitignored -
|
||||
│ # read by no command in the ordinary pipeline; a human reviews it with
|
||||
│ # `wikitool upload list/show/accept/reject`
|
||||
├── incoming/ # INBOX: gitignored and flat - drop a file here, `raw accept` promotes it
|
||||
├── incoming/ # INBOX: flat, content gitignored - drop a file here, `raw accept` promotes it
|
||||
├── raw/ # INPUT: immutable, untrusted source material
|
||||
│ ├── CONTRACT.md # Date shard, capture fields, immutability, untrusted content
|
||||
│ ├── 2026/09/ # Where `raw accept` puts a file: the month it was accepted
|
||||
|
||||
@@ -28,24 +28,13 @@ they are published: the agent harness will not offer `wiki-ingest`, `wiki-query`
|
||||
cd ..
|
||||
```
|
||||
|
||||
2. **Create the ingest inbox.** `incoming/` (raw/CONTRACT.md "Getting a file in") is gitignored,
|
||||
so a fresh clone does not have it at all - unlike `raw/` itself, which is committed and
|
||||
present immediately. It is flat - there is no type subdirectory to create:
|
||||
|
||||
```bash
|
||||
mkdir -p incoming
|
||||
```
|
||||
|
||||
`tools/wikitool doctor` only reports a missing one; it never creates it, so this is a one-off
|
||||
catch-up here the same way step 5 below is for personalization.
|
||||
|
||||
3. **Publish the skills:**
|
||||
2. **Publish the skills:**
|
||||
|
||||
```bash
|
||||
tools/wikitool instructions sync
|
||||
```
|
||||
|
||||
4. **Verify:**
|
||||
3. **Verify:**
|
||||
|
||||
```bash
|
||||
tools/wikitool instructions verify
|
||||
@@ -54,14 +43,14 @@ they are published: the agent harness will not offer `wiki-ingest`, `wiki-query`
|
||||
Expected: `OK`. If it reports drift, re-run `sync` - the source under `instructions/` always
|
||||
wins, and a copy is never edited directly.
|
||||
|
||||
5. **Check for personalization.** A clone predating the personalization files has no
|
||||
4. **Check for personalization.** A clone predating the personalization files has no
|
||||
`USER.md`/`SOUL.md`, and `tools/wikitool doctor` reports `personalization: FAIL` for it.
|
||||
That is a one-off catch-up, not a bootstrap step that repeats: run **only** the
|
||||
Personalization step (6) of [setup-instance.md](setup-instance.md), not the whole
|
||||
procedure - this clone already has its git repo, author identity and content. A clone that
|
||||
already carries both files needs nothing here.
|
||||
|
||||
6. **Offer to record the environment.** `ENVIRONMENT.md` is gitignored, so a fresh clone never
|
||||
5. **Offer to record the environment.** `ENVIRONMENT.md` is gitignored, so a fresh clone never
|
||||
has one, and every session in it re-asks which harness is in use, which MCP servers are
|
||||
reachable, and which remote `publish` talks to. Copy `ENVIRONMENT.md.template` to
|
||||
`ENVIRONMENT.md`, fill in what is already known from this clone (`git remote -v`, the
|
||||
@@ -73,15 +62,15 @@ they are published: the agent harness will not offer `wiki-ingest`, `wiki-query`
|
||||
session pays for it again. Never guess an entry: a wrong remote or an MCP server that is not
|
||||
there is worse than the empty section it replaced, because it gets believed.
|
||||
|
||||
7. **Restart the agent session** if it was already running. Harnesses read the skill
|
||||
6. **Restart the agent session** if it was already running. Harnesses read the skill
|
||||
directories at startup, so skills published mid-session are not picked up.
|
||||
|
||||
8. **Expect a lingering `session-id` WARN.** A `tools/wikitool doctor` run at this point reports
|
||||
7. **Expect a lingering `session-id` WARN.** A `tools/wikitool doctor` run at this point reports
|
||||
`OK` throughout except `session-id: WARN` - that check is scoped to the working session, not
|
||||
the clone, so a freshly bootstrapped checkout with no `WIKITOOL_SESSION_ID` exported yet
|
||||
always shows it. This is expected, not a Bootstrap gap: exporting it here would only be true
|
||||
for this one-off setup run, not for whichever session picks up the actual work next, in a new
|
||||
shell after step 7's restart. Run [session-setup.md](session-setup.md) at the start of that
|
||||
shell after step 6's restart. Run [session-setup.md](session-setup.md) at the start of that
|
||||
session instead.
|
||||
|
||||
## Scope
|
||||
|
||||
+3
-2
@@ -63,8 +63,9 @@ walks `raw/` recursively and works unchanged either way.
|
||||
|
||||
## Getting a file in: `incoming/`
|
||||
|
||||
`raw/` is never chosen by hand. A file to be ingested is dropped into the gitignored top-level
|
||||
`incoming/` - flat: no subdirectory carries any classification any more. A
|
||||
`raw/` is never chosen by hand. A file to be ingested is dropped into the top-level `incoming/`
|
||||
- gitignored content, so a fresh clone finds the directory itself already there but never
|
||||
anything dropped into it - flat: no subdirectory carries any classification any more. A
|
||||
subdirectory is still tolerated if one is used out of habit or by an older script (so an upgrade
|
||||
never has to touch a caller), but it is **ignored**, never inspected:
|
||||
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ tools/wikitool <command> --help
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `dist export <target> [--dry-run] [--source-repo U] [--source-commit SHA] [--release-url U] [--update-url U]` | Write a contentless, distributable copy of this repo's machinery into an empty `<target>` directory: `AGENTS.md`/`README.md`/`EVALS.md` with any `<!-- dist:strip-start -->...<!-- dist:strip-end -->` region removed, `instructions/` (minus `instructions/dev/`), `types/` (the `root: kb` page type-specs and their schemas re-keyed as `.template`, the stack's own verbatim), `docs/` verbatim, `tools/` (no venv/caches), the `.github/hooks/`+`.vibe/` session-tracing config plus `.claude/settings.json`, `kb/CONTRACT.md` (no pages, no areas), empty `raw/{articles,documents,notes,assets}/` and the matching `incoming/{articles,documents,notes,assets}/` (gitignored again the moment the export becomes a git repository, so `instructions/bootstrap.md` re-creates it for a plain clone that never ran this step), `VERSION`, `USER.md.template`/`SOUL.md.template` plus `kb/CONVENTIONS.md.template` and each collection's contract re-keyed as `kb/<name>/COLLECTION.md.template` (the templates ship; the filled `USER.md`/`SOUL.md`/`kb/CONVENTIONS.md`/`kb/<name>/COLLECTION.md`/`types/<page-type>.md` never do - all of them bind their instance and none are the stack's to decide, and `find_leaks` refuses a plan carrying one), and a generated `.wikitool-release.json` stamp (version, export date, origin, and a sha256 per exported file - the base a later upgrade would compare against). The four origin options only fill stamp fields: `export` never calls git and cannot discover them. Refuses a non-empty target, and a tree with no `VERSION`. See `instructions/setup-instance.md`. One-way: there is no command that reconstructs a distributed instance into a dev instance - work on the stack in the origin repo (or a new dev instance exported from it) instead |
|
||||
| `dist export <target> [--dry-run] [--source-repo U] [--source-commit SHA] [--release-url U] [--update-url U]` | Write a contentless, distributable copy of this repo's machinery into an empty `<target>` directory: `AGENTS.md`/`README.md`/`EVALS.md` with any `<!-- dist:strip-start -->...<!-- dist:strip-end -->` region removed, `instructions/` (minus `instructions/dev/`), `types/` (the `root: kb` page type-specs and their schemas re-keyed as `.template`, the stack's own verbatim), `docs/` verbatim, `tools/` (no venv/caches), the `.github/hooks/`+`.vibe/` session-tracing config plus `.claude/settings.json`, `kb/CONTRACT.md` (no pages, no areas), empty `raw/{articles,documents,notes,assets}/` and the matching `incoming/{articles,documents,notes,assets}/` (`incoming/.gitkeep` is trackable and survives becoming a git repository, so a plain clone gets the directory without any bootstrap step re-creating it), `VERSION`, `USER.md.template`/`SOUL.md.template` plus `kb/CONVENTIONS.md.template` and each collection's contract re-keyed as `kb/<name>/COLLECTION.md.template` (the templates ship; the filled `USER.md`/`SOUL.md`/`kb/CONVENTIONS.md`/`kb/<name>/COLLECTION.md`/`types/<page-type>.md` never do - all of them bind their instance and none are the stack's to decide, and `find_leaks` refuses a plan carrying one), and a generated `.wikitool-release.json` stamp (version, export date, origin, and a sha256 per exported file - the base a later upgrade would compare against). The four origin options only fill stamp fields: `export` never calls git and cannot discover them. Refuses a non-empty target, and a tree with no `VERSION`. See `instructions/setup-instance.md`. One-way: there is no command that reconstructs a distributed instance into a dev instance - work on the stack in the origin repo (or a new dev instance exported from it) instead |
|
||||
| `dist upgrade <source> [--dry-run] [--keep-local] [--prune] [--pre]` | Apply a stack update `dist export` produced - the write half of `version check`. Never downloads anything: `<source>` is an already-fetched export directory or `.tar.gz` release archive (verified against a sibling `.sha256` if one is present; WARNs, does not block, if it is absent), which must unpack to exactly one top-level directory - the shape `.gitea/workflows/release.yml` packs. The write set is exactly the *new* `.wikitool-release.json`'s `files` block, minus what an export re-seeds from a blank template every time (`kb/log.md`, `raw/*/.gitkeep` - `chemenu.ownership.is_export_stub`) or seeds once and the instance owns from then on (`.wikitool-kb.json`, `CHANGES.md` - `chemenu.ownership.is_upgrade_preserved`), plus the stamp itself, always rewritten. Every candidate path is classified against the *local* `.wikitool-release.json`'s recorded digest for it: unchanged is overwritten silently, absent from the old stamp is created, and locally modified or locally deleted is **never** silently overwritten - the run aborts with the full list unless `--keep-local` says to proceed and leave every one of them untouched. After a `--keep-local` run the new stamp is still written whole, so it records the release's digest for files that were deliberately *not* written: the stamp is the baseline for the next comparison, not a literal inventory of what is on disk. That is what keeps a skipped file diverging - and therefore reported - on every later run, rather than quietly reading as current once it has been skipped once. A path in the old stamp but not the new one is reported as no longer part of the release and left alone unless `--prune` is passed, which removes it only if it is still unchanged since installation. Reports the migration chain the new machinery would owe (`chemenu.kb_state.chain` over the *new* tree's `instructions/migrations/`, read via a `directory` argument to `load_migrations`) but never runs any of it - there is no `migrate run`. Refuses before touching the source at all when: `VERSION` or `.wikitool-release.json` (with a `files` block) is missing locally, `.wikitool-kb.json` is missing, a migration is already outstanding against the *installed* machinery, or the working tree is dirty (not being a git repository at all is a WARN, not a refusal). Refuses after reading the source when: it carries no `VERSION`/`.wikitool-release.json`/`files` block, its version is older than or equal to the installed one (equal is a no-op success), or it is a pre-release (`-beta.N`) without `--pre`. Reports, but does not block on, a crossed compatibility boundary. Never touches git - no commit, no push (invariant 5). See `INSTALL.md` § "Eine Instanz aktualisieren" |
|
||||
| `version show [--json]` | Print this instance's stack version and where it came from (development tree, or a distribution with its export date and origin). Bare `wikitool version` is an alias for this. Read-only, offline, and **exempt from the Iteration Budget Gate** |
|
||||
| `version check [--url U] [--timeout S] [--json]` | Ask the origin's release feed whether a newer stack exists, and whether the step crosses a compatibility boundary (`state: current\|update\|migration\|ahead`). **The only command in `wikitool` that makes a network call** - never reached implicitly from another command, needs no key, times out, and reports an unreachable feed as an error rather than as "up to date". The feed is `$WIKITOOL_UPDATE_URL`, else the release stamp's, else the built-in origin; `$WIKITOOL_UPDATE_TOKEN` is only needed if that feed is not readable anonymously. Read-only and exempt from the budget gate |
|
||||
|
||||
@@ -143,6 +143,14 @@ REQUIRED_TRACKED_PATHS = (
|
||||
# has nothing to fill in - and `find_leaks` refuses to substitute the filled
|
||||
# file, correctly, so the export would simply be missing it.
|
||||
"kb/CONVENTIONS.md.template",
|
||||
# The ingest inbox's one anchor file (Gitea #88). `/incoming/*` ignores
|
||||
# everything else dropped there, same as the old `/incoming/` did - but
|
||||
# unlike that form, a directory pattern, this one lets a negation actually
|
||||
# re-include a single file, so a fresh clone gets the directory without
|
||||
# `instructions/bootstrap.md` recreating it by hand. A future return to
|
||||
# the directory form would silently drop this file again; this canary is
|
||||
# what makes that regression fail loudly instead.
|
||||
"incoming/.gitkeep",
|
||||
)
|
||||
|
||||
CLI_README = config.ROOT / "tools" / "CONTRACT.md"
|
||||
|
||||
Reference in New Issue
Block a user