diff --git a/CHANGES.md b/CHANGES.md index 471d00b..b30a524 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -35,7 +35,7 @@ dev-checkout concern - readable here, never shipped as something to parse. --- -## 4.8.0-beta.9 - 2026-09-09 - source_type ist Instanzsache: Profilkatalog, Setup-Frage, evolve-subtypes-Instruction (#68) +## 4.8.0-beta.10 - 2026-09-09 - instructions/CONTRACT.md: Skill-H1, Referenztiefe und Begruendungsprosa praezisiert (#71, #72, #79) **Author:** Torben Nehmer @@ -50,6 +50,7 @@ dev-checkout concern - readable here, never shipped as something to parse. - source_type: Default streichen, unclassified als sichtbares Fach, layout: fuer source (schliesst #66) - raw accept: Datums-Shard statt Typverzeichnis, fidelity/authority am Drop-Punkt (schliesst #67) - source_type ist Instanzsache: Profilkatalog, Setup-Frage, evolve-subtypes-Instruction (#68) +- instructions/CONTRACT.md: Skill-H1, Referenztiefe und Begruendungsprosa praezisiert (#71, #72, #79) Das Label `status/incoming` gibt es seit heute in Gitea: der Mensch legt einen @@ -579,6 +580,74 @@ Geändert: `instructions/kb-profiles.md`, `instructions/setup-instance.md`, Schließt #68. +### `instructions/CONTRACT.md` § Writing an instruction: drei offene Fragen entschieden + +Drei Issues aus der #65-Analyse zeigten auf denselben Abschnitt. Alle drei +enden dort, wo die Regel steht, nicht in einem Issue-Kommentar. + +**Der Imperativ-Titel bindet eine Instruction, nicht ein `SKILL.md` (#71).** +Die Regel griff dem Wortlaut nach auf alle fünf Skills durch, deren H1 +Nomenphrasen sind. Geprüft gegen die Primärquelle: Anthropic normiert `name` +und `description` und sagt zur Body-Überschrift nichts; die eigenen +Beispiel-Skills heißen `# PDF Processing`, `# BigQuery Data Analysis`. Dazu +das Sachargument — der H1 liegt auf keinem Retrieval-Pfad, weil über die +Aufnahme eines Skills die `description` entscheidet, die ab Sessionstart im +Kontext steht, während der Body erst beim Zugriff gelesen wird. In der Sitzung +kam ein Beleg dazu, den keines der Issues kannte: der vendorierte +`commonplace`-Korpus trägt dieselbe Imperativ-Titel-Regel, unabhängig +entstanden, und macht im selben Absatz dieselbe Ausnahme („for promoted skills, +the skill name is the title"). Die fünf Titel bleiben unverändert. + +**Referenztiefe: Anthropics „one level deep" gilt gebündeltem Material (#72).** +Weg 2 der drei zur Wahl stehenden. Der Beleg für die Reichweite steht im +vendorierten `codex-skill-creator/SKILL.md`: die Beispiele der Regel sind +`DOCX-JS.md`, `REDLINING.md`, `OOXML.md` — alles Dateien *im Skill-Bündel*. Kein +Skill dieses Repos hat heute eine solche Datei, die Regel bindet hier also +wörtlich nichts. Für den Link von einem Skill auf einen repo-weiten Contract +fallen die beiden Hälften der Frage auseinander: die Mechanik (Zweit-Hop wird +womöglich nur angelesen) ist real und verzeichnisunabhängig, die Vorschrift ist +für diesen Fall von Anthropic nicht belegt. Die geteilten Contracts bleiben +geteilt — Invariante 8 hat sie dorthin gestellt, und § Frontload verlangt, dass +ein *Schritt* ohne Vorkontext entscheidbar ist, nicht dass jede Regel an ihm +wiederholt wird. Als Auflage bleibt das Billigere: ein Link sagt, was der +Schritt aus der Datei braucht. + +Ob die Mechanik hier überhaupt beißt, wurde vor der Entscheidung im Eval-Aufbau +nachgesehen, und die Antwort ist: nicht messbar. Die L2-Trajectory-Regeln lesen +ausschließlich `wikitool.call`, `gate.*`, `publish.commit` und +`prompt.submitted` — keine Dateizugriffe eines Agenten; auf Claude Code ist +überhaupt kein Tool-Hook verdrahtet, ein `head -100` hinterlässt also keine +Spur. Die zweite Hälfte der Behauptung, was am Ende im Kontextfenster stand, +erzeugt konstruktionsbedingt nirgends ein Event. Ein kausaler A/B-Vergleich +bräuchte den L3-Runner, der entworfen und nicht gebaut ist. Das steht jetzt im +Contract: eine Festlegung über Reichweite, keine Messung. Der Nebenfund — L2 +sieht auf dem primären Harness gar keine Tool-Calls — ist ein eigenes Issue +wert und nicht Teil dieser Änderung. + +**Wieviel Begründung ein Schritt tragen darf, ist jetzt messbar (#79).** Der +alte Satz („Keep reasoning out of the body […] keep only enough reasoning to +decide edge cases") zog in zwei Richtungen, und die größte Instruction des +Repos lebte in der Lücke. Neu sind eine Keep/Cut-Tabelle und zwei Tests: +*Substitution* — die Passage streichen und den Schritt noch einmal lesen; rät +ein Agent ohne Vorkontext jetzt, war es eine Entscheidungshilfe und sie bleibt, +egal wie lang. *Once* — eine Entscheidungshilfe steht an dem Schritt, an dem die +Entscheidung fällt, und an genau einem solchen (Invariante 8). Danach gemessen +stand die `source_type`/Capture-Asymmetrie in `wiki-ingest` zweimal; sie steht +jetzt einmal, in Schritt 1, und Schritt 6 trägt die Anweisung plus Verweis. +Die beiden anderen in #79 genannten Stellen — Namenskollision in Schritt 1, +`## Not Extracted` in Schritt 6 — bestehen den Substitutionstest und bleiben. +214 → 211 Zeilen; die Kürzung ist nicht der Zweck, die Eindeutigkeit ist es. + +**PATCH**, geprüft gegen den Drop-in-Test: eine Instanz kopiert +`instructions/` über sich, nichts wird umbenannt oder entfernt, kein Kommando, +kein Flag, kein maschinengelesenes Format, und der Rückweg funktioniert +genauso. Kein `--breaking`, kein Migrationsdokument. + +Geändert: `instructions/CONTRACT.md` (§ Writing an instruction, drei neue +Unterabschnitte), `instructions/wiki-ingest/SKILL.md` (Schritte 1 und 6). + +Schließt #71, #72 und #79. + --- ## 4.7.4 - 2026-09-04 - bootstrap.md nennt den session-id-WARN nach frischem Bootstrap explizit als erwartet diff --git a/VERSION b/VERSION index 67708b3..a58ddb9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.8.0-beta.9 +4.8.0-beta.10 diff --git a/instructions/CONTRACT.md b/instructions/CONTRACT.md index a65e3d8..8fbf9ce 100644 --- a/instructions/CONTRACT.md +++ b/instructions/CONTRACT.md @@ -142,16 +142,105 @@ does not survive being archived or copied. The price of a copy is drift, and dri Scaffold with `tools/wikitool new instruction --name ""`; the contract is `tools/wikitool types describe instruction`. -- **Imperative title.** It answers "what does this tell me to do?". +- **Imperative title.** It answers "what does this tell me to do?". This binds the flat + `instructions/.md` form only - a skill's H1 is a different case, below. - **`description` is the retrieval wire.** Write it to match the question an agent would ask when it needs this procedure, not as a label for the file. - **Frontload.** Self-contained enough for an agent with no prior context: define terms - inline, do not assume other documents are loaded. -- **Keep reasoning out of the body.** Cut the explanation of *why* each step exists. If it is - worth preserving, it is a concept page under `kb/concepts/`, linked from here. Keep only - enough reasoning to decide edge cases. + inline, do not assume other documents are loaded. What this does and does not say about + linking a shared contract: below. +- **Reasoning earns its place by deciding something.** Keep what an agent needs in order to get + *this* decision right, at the step where it falls; cut the explanation of why the step exists + at all. If that is worth preserving, it is a concept page under `kb/concepts/`, linked from + here. Where the line runs, and how to test a passage against it: below. - **State scope boundaries.** When does this *not* apply, and what to do instead. +### A skill's H1 is a name, not an imperative + +`instructions//SKILL.md` takes a name-shaped H1 matching its `name:` frontmatter - +`# Wiki Ingest`, not `# Ingest a source file into the wiki`. The imperative-title rule is +written for the flat form and stops there. + +The heading lies on no retrieval path. What decides whether a skill is picked up is +`description`, which sits in the agent's context from session start; the body is read only once +the skill is already open, and by then the title has nothing left to decide. Anthropic's +skill-authoring guidance agrees by omission and by example: it normalises `name` and +`description` and says nothing about the body's heading, and its own worked examples are noun +phrases (`# PDF Processing`, `# BigQuery Data Analysis`). So does the vendored `commonplace` +corpus, which arrived at the imperative-title rule independently and carves out the same +exception in the same breath - "for promoted skills, the skill name is the title". + +That is the whole exception. Everything else in this section binds a `SKILL.md` exactly as it +binds an instruction. + +### Reference depth: bundled files, not repo-wide contracts + +Anthropic's skill-authoring guidance asks that reference files stay **one level deep from +`SKILL.md`**, because a file reached at the second hop may be previewed rather than read - +`head -100` instead of the whole file - leaving the step to run on incomplete information. + +That rule governs **skill-bundled** material: files sitting in `instructions//` beside the +`SKILL.md`. The guidance's own worked example is a bundle (`SKILL.md` → `REDLINING.md`, +`OOXML.md`), and it says nothing about files outside the skill directory. No skill in this repo +has a bundled file today, so as written the rule currently binds nothing here. + +A link from a skill to a repo-wide contract - [kb/CONTRACT.md](../kb/CONTRACT.md), +[tools/CONTRACT.md](../tools/CONTRACT.md), [gates.md](gates.md) - is a different category, and +the two halves of the question have different answers: + +- **The mechanic is real and directory-independent.** A contract reached at the second hop can + be read partially exactly as a bundled file would be. Nothing about the path makes it safe. +- **The rule is not.** Reading its scope wider than it states would attribute a rule to a source + that does not carry it - the same move invariant 3 forbids about facts. + +So the shared contracts stay shared and stay linked once. AGENTS.md invariant 8 is what put them +there: copying `kb/CONTRACT.md` into five `SKILL.md` files is precisely the second copy that +drifts. § Frontload does not ask for that either - it asks that a **step** be decidable without +prior context, not that every rule the step obeys be restated at it. + +What the mechanic does oblige is cheaper than either: **a link says what the step needs from the +file it points at.** A bare "read X first" leaves a partial read undetectable; naming what is to +be taken from it - the field, the section, the decision - keeps the step decidable even when the +read came up short, and tells the next author which reference is actually load-bearing. +`wiki-ingest` step 7 is the shape: three contracts linked, each with the clause that says why +this step needs it. + +This is a narrower posture than the vendored `commonplace` corpus takes, which makes outbound +links exceptional in its instruction collection and frontloads the rest. That works for a corpus +whose procedures do not share a contract; here they do, and invariant 8 outranks the preview +risk. + +**All of the above is a judgment, not a measurement**, and it is worth knowing why it cannot be +the second. Whether the mechanic bites here is not something this repo can currently observe: +the L2 trajectory scorers read `wikitool` calls, gates and publishes, never an agent's file +reads, and no tool hook is wired on the primary harness at all - so a `head -100` leaves nothing +to score. The other half of the claim, what ended up in the context window, produces no event +anywhere by construction. Gitea #72 records what a test would cost and why it was not bought. + +### How much reasoning a step may carry + +"Cut the reasoning" and "keep enough to decide an edge case" are one sentence pulling two ways, +and the largest instruction in this repo lives in the gap. The line runs here: + +| Keep | Cut | +|---|---| +| What an agent must know to get this decision right, at the step where it falls | Why the step exists at all | +| The consequence of the wrong choice, when nothing later catches it | The consequence, when a validator, a gate or a later step catches it | +| Why a plausible-looking default is the wrong answer | Background about the design that produced the field | + +Two tests, both cheap: + +- **Substitution.** Delete the passage and read the step again. Does an agent with no prior + context still make the same call? If yes, it was background. If it now guesses, it was a + decision aid, and it stays - however long it runs. +- **Once.** A decision aid belongs at the step where the decision falls, and at exactly one such + step (AGENTS.md invariant 8). Where the same decision falls at two steps - `wiki-ingest` asks + for `fidelity`/`authority` in step 1 and again in step 6 - the reasoning is written at the + first and the second carries the instruction plus a pointer, never a second telling. + +A passage that survives both is not an exception to the rule. Deciding an edge case is the part +the rule keeps; the length it takes to do that is not the measure. + ## Instruction duality These files are both content and running system. Changing one changes agent behaviour diff --git a/instructions/wiki-ingest/SKILL.md b/instructions/wiki-ingest/SKILL.md index c4617a0..a243eb6 100644 --- a/instructions/wiki-ingest/SKILL.md +++ b/instructions/wiki-ingest/SKILL.md @@ -24,13 +24,10 @@ pages should never have cost the concept contract. Field-level requirements alwa two capture flags are not: `raw accept` refuses without them. **Ask the user for `--fidelity` and `--authority` before this call, rather than guessing from - a quick look at the file.** This is the one place in the whole skill where the asymmetry from - `source_type` (step 6 below) does not hold: a wrong `source_type` sits in a visible, - correctable catalog slot (`unclassified`), but a guessed capture value is not "unknown" - it - is a claim about the capture that cannot be corrected later, because the knowledge exists only - at this drop point. If it is genuinely unclear how faithful the capture is or what the - material may claim about its subject, say so to the user and ask directly - do not fall back - to a plausible-looking default. + a quick look at the file.** A guessed capture value is not "unknown": it is a claim about the + capture that nothing later can correct, because the knowledge exists only at this drop point. + Genuinely unclear how faithful the capture is, or what the material may claim about its + subject? Say so and ask - there is no plausible-looking default to fall back on. ```bash tools/wikitool raw accept --fidelity --authority \ @@ -79,7 +76,8 @@ pages should never have cost the concept contract. Field-level requirements alwa which entities/concepts to create or update, any specific emphasis. 6. **Create the source page.** Read - [kb/sources/COLLECTION.md](../../kb/sources/COLLECTION.md) first. + [kb/sources/COLLECTION.md](../../kb/sources/COLLECTION.md) first - it holds what this + instance expects of a source page's sections and how it names one. ```bash tools/wikitool new source --name "" \ @@ -99,12 +97,12 @@ pages should never have cost the concept contract. Field-level requirements alwa it later without moving or renaming the page. `fidelity` and `authority` have no default either, and `new source` refuses without them the - same way (Gitea #67) - but unlike `source_type` there is no catalog slot to fall back on. If - step 1 already ran `raw accept` without `--page`, its success message printed the exact - `--set fidelity=... --set authority=...` pair to reuse here verbatim; if it did not (the file - was already in `raw/`), ask the user the same question step 1 asks before promoting, rather - than inferring an answer from the file's content now. Never pass `unknown` here - that value - is backfill-only, written only by `wikitool touch` on a page predating this rule. + same way (Gitea #67) - but here there is no catalog slot to fall back on, for the reason step + 1 gives. If step 1 already ran `raw accept` without `--page`, its success message printed the + exact `--set fidelity=... --set authority=...` pair to reuse here verbatim; if it did not (the + file was already in `raw/`), ask the user, rather than inferring an answer from the file's + content now. Never pass `unknown` here - that value is backfill-only, written only by + `wikitool touch` on a page predating this rule. List **every** raw file this ingest covers - a folder of related documents becomes one source page with all its files in `raw_files:`, not one page per file. For an external