types/: Seiten-Type-Spec-Anleitungsprosa in stackeigene guidance-Datei ausgelagert (schliesst #104)
Files changed: - AGENTS.md - CHANGES.md - VERSION - docs/ownership-and-templates.md - instructions/migrations/6.0.0-type-guidance-split.md - instructions/setup-instance.md - tools/CONTRACT.md - tools/chemenu/commands/dist_cmd.py - tools/chemenu/commands/types_cmd.py - tools/chemenu/tests/test_dist_cmd.py - tools/chemenu/tests/test_dist_upgrade.py - tools/chemenu/tests/test_type_resolver.py - tools/chemenu/tests/test_types_cmd.py - tools/chemenu/type_resolver.py - tools/chemenu/types_core.py - types/comparison.guidance.md - types/comparison.md - types/concept.guidance.md - types/concept.md - types/entity.guidance.md - types/entity.md - types/source.guidance.md - types/source.md - types/type-guidance.md - types/type-guidance.schema.yaml - types/type-spec.md
This commit is contained in:
@@ -99,6 +99,7 @@ What a file is called says who it is for and how it is loaded. This is a rule, n
|
|||||||
| `instructions/<name>.md` | Agents | By link, or on explicit request |
|
| `instructions/<name>.md` | Agents | By link, or on explicit request |
|
||||||
| `instructions/<name>/SKILL.md` | Agents | By the harness, once published |
|
| `instructions/<name>/SKILL.md` | Agents | By the harness, once published |
|
||||||
| `types/<name>.md` | Agents + validator | Via `tools/wikitool types describe`. Split by `root:`: a page type-spec (`root: kb`) belongs to the instance and ships as `.template`; one describing a stack artifact ships verbatim |
|
| `types/<name>.md` | Agents + validator | Via `tools/wikitool types describe`. Split by `root:`: a page type-spec (`root: kb`) belongs to the instance and ships as `.template`; one describing a stack artifact ships verbatim |
|
||||||
|
| `types/<name>.guidance.md` | Agents + validator | Via `tools/wikitool types describe`, composed with the `types/<name>.md` it documents. Stack-owned regardless of the type-spec's own `root:` - it ships verbatim and is optional, present only where the type-spec declares `guidance:` |
|
||||||
| `docs/<name>.md` | Agents and humans | By link, or on explicit request - never automatically, and never as instruction |
|
| `docs/<name>.md` | Agents and humans | By link, or on explicit request - never automatically, and never as instruction |
|
||||||
| `INDEX.md` | Both | Generated - never hand-edited |
|
| `INDEX.md` | Both | Generated - never hand-edited |
|
||||||
|
|
||||||
|
|||||||
+44
-1
@@ -59,7 +59,7 @@ concern - readable here, never shipped as something to parse.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6.0.0-beta.5 - 2026-09-15 - Control-Plane-Sprache universell: Achse ist das Publikum, kein Instanz-Schalter
|
## 6.0.0-beta.6 - 2026-09-15 - types/: Seiten-Type-Spec-Anleitungsprosa in stackeigene guidance-Datei ausgelagert
|
||||||
|
|
||||||
**Author:** Torben Nehmer
|
**Author:** Torben Nehmer
|
||||||
|
|
||||||
@@ -74,6 +74,7 @@ concern - readable here, never shipped as something to parse.
|
|||||||
- SKILL.md: relative Links durch repo-root-relative Pfade ersetzt, docs verify/instructions verify pruefen Linkziele
|
- SKILL.md: relative Links durch repo-root-relative Pfade ersetzt, docs verify/instructions verify pruefen Linkziele
|
||||||
- docs verify: der Linkziel-Check erreicht auch die instanz-eigenen kb/CONVENTIONS.md und COLLECTION.md - daher Grenzuebertritt
|
- docs verify: der Linkziel-Check erreicht auch die instanz-eigenen kb/CONVENTIONS.md und COLLECTION.md - daher Grenzuebertritt
|
||||||
- TOC-Scope auf types/ und docs/ erweitert, Sprachregeln zentralisiert, --breaking akkumuliert
|
- TOC-Scope auf types/ und docs/ erweitert, Sprachregeln zentralisiert, --breaking akkumuliert
|
||||||
|
- types/: Seiten-Type-Spec-Anleitungsprosa in stackeigene guidance-Datei ausgelagert
|
||||||
|
|
||||||
**Medium impact**
|
**Medium impact**
|
||||||
- docs verify: ein nur als .template ausgeliefertes Linkziel gilt als aufgeloest
|
- docs verify: ein nur als .template ausgeliefertes Linkziel gilt als aufgeloest
|
||||||
@@ -264,6 +265,48 @@ Verzeichnis fuenf trug: `docs/model-and-effort-selection.md` fehlte, und zwar ab
|
|||||||
ein Link dorthin die Claude-Code-eigene Entscheidung in die anderen drei Harnesses laden wuerde.
|
ein Link dorthin die Claude-Code-eigene Entscheidung in die anderen drei Harnesses laden wuerde.
|
||||||
Der Satz zaehlt jetzt, was von hier aus verlinkt ist, und benennt die sechste Seite samt Grund.
|
Der Satz zaehlt jetzt, was von hier aus verlinkt ist, und benennt die sechste Seite samt Grund.
|
||||||
|
|
||||||
|
### types/: Seiten-Type-Spec-Anleitungsprosa in stackeigene guidance-Datei ausgelagert
|
||||||
|
|
||||||
|
Ein `root: kb` Type-Spec (`entity`, `concept`, `source`, `comparison`) hatte zwei Publika in
|
||||||
|
einer Datei: Anleitungsprosa fuer den Agenten (When to use/When NOT to use/Authoring guidance),
|
||||||
|
und Seitenmaterial (`## Template`-Block, `layout:`-Titel). Ownership gilt pro Datei, also wurde
|
||||||
|
die ganze Datei beim Setup als `.template` adoptiert und danach nie wieder angefasst - eine
|
||||||
|
Instanz, die ihre Type-Specs frueh adoptiert hat, las bis in alle Zukunft die Anleitung vom Tag
|
||||||
|
ihrer Erzeugung, weil `dist upgrade` das `.template` neben die adoptierte Datei schrieb, nie die
|
||||||
|
Datei selbst (`docs/ownership-and-templates.md` § "Where the file boundary strains").
|
||||||
|
|
||||||
|
Der urspruengliche Vorschlag drehte den Schnitt um (Type-Spec stackeigen, Seitenmaterial heraus)
|
||||||
|
und wurde beim Pruefen gegen `setup-instance.md` und `evolve-subtypes.md` verworfen: die
|
||||||
|
Frontmatter-Konfiguration (`layout:`, Enum-Werte, `base_dir`) ist instanzeigener Inhalt, keine
|
||||||
|
Stack-Maschinerie - beide Instructions weisen die Instanz an, Enum und `layout:`-Eintrag in
|
||||||
|
derselben Aenderung zu setzen. Stattdessen bleibt der Type-Spec instanzeigen, und nur die
|
||||||
|
maschinenabgeleitete Anleitungsprosa zieht in eine neue, stackeigene `types/<name>.guidance.md`,
|
||||||
|
verknuepft ueber ein optionales `guidance:`-Frontmatterfeld (neuer, nicht instanziierbarer Typ
|
||||||
|
`type-guidance`, wie `lint-report` ohne `base_dir:`). `tools/wikitool types describe <name>`
|
||||||
|
komponiert beide Haelften weiterhin zu einer Antwort - ein Agent muss nie wissen, dass ein Typ aus
|
||||||
|
zwei Dateien besteht. `type_resolver.extract_template()` liest das Template unveraendert allein
|
||||||
|
aus `types/<name>.md`; kein zweiter Ladepfad fuer `wikitool new`.
|
||||||
|
|
||||||
|
`dist_cmd._plan_types()`/`find_leaks()` teilten sich vorher `name.split(".", 1)[0]` als
|
||||||
|
Stamm-Berechnung - beides haette `entity.guidance.md` faelschlich als instanzeigenen Stamm
|
||||||
|
"entity" erkannt (die eine haette sie zum `.template` gemacht, die andere sie als Leak gemeldet).
|
||||||
|
Neuer gemeinsamer Prädikat `_owned_type_stem()` prueft die exakte Endung (`<stem>.md` oder
|
||||||
|
`<stem>.schema.yaml`), nicht den ersten Punkt.
|
||||||
|
|
||||||
|
Grenzuebertritt-Frage bewusst geprueft und verneint: Drop-in in beide Richtungen (ein Type-Spec
|
||||||
|
ohne `guidance:` verhaelt sich unveraendert, eine alte Maschinerie liest `types/<name>.md` wie
|
||||||
|
zuvor und die Guidance-Datei ist fuer sie inert), also `--minor` statt `--major`. Die einmalige
|
||||||
|
Adoption in einer bestehenden Instanz ist als `instructions/migrations/6.0.0-type-guidance-split.md`
|
||||||
|
dokumentiert - `obligation: offered`, der erste Gebrauch dieses seit 4.0.0 existierenden, bis jetzt
|
||||||
|
unbenutzten Mechanismus fuer ein instanzeigenes, upgradebares Machinery-File.
|
||||||
|
|
||||||
|
Verifiziert: `tools/wikitool docs verify`/`instructions verify` gruen, 1261 Tests gruen (8 neu:
|
||||||
|
`get_guidance`, das Template bleibt auf `types/<name>.md` allein geladen, die Guidance-Datei
|
||||||
|
schifft verbatim neben einem `.template`-adoptierten Type-Spec statt als weiteres `.template`,
|
||||||
|
ein `dist upgrade` schreibt verbesserte Guidance-Prosa in eine adoptierte Instanz obwohl deren
|
||||||
|
Type-Spec selbst nie im Stamp stand, `types describe` komponiert beide Haelften in JSON und
|
||||||
|
Textausgabe getrennt nachweisbar).
|
||||||
|
|
||||||
## 5.1.0 - 2026-09-12 - changelog: Kandidaten-Eintrag nach Impact gruppiert, version regrade zur Nachkorrektur, version release verlangt eine Zusammenfassung
|
## 5.1.0 - 2026-09-12 - changelog: Kandidaten-Eintrag nach Impact gruppiert, version regrade zur Nachkorrektur, version release verlangt eine Zusammenfassung
|
||||||
|
|
||||||
**Author:** Torben Nehmer
|
**Author:** Torben Nehmer
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ overwriting them would silently erase a choice someone made on purpose.
|
|||||||
- [Why silent overwrite is the failure being designed against](#why-silent-overwrite-is-the-failure-being-designed-against)
|
- [Why silent overwrite is the failure being designed against](#why-silent-overwrite-is-the-failure-being-designed-against)
|
||||||
- [Why the boundary is a predicate rather than a list](#why-the-boundary-is-a-predicate-rather-than-a-list)
|
- [Why the boundary is a predicate rather than a list](#why-the-boundary-is-a-predicate-rather-than-a-list)
|
||||||
- [Why a `.template`, not just an absent file](#why-a-template-not-just-an-absent-file)
|
- [Why a `.template`, not just an absent file](#why-a-template-not-just-an-absent-file)
|
||||||
- [Where the file boundary strains](#where-the-file-boundary-strains)
|
- [Where the file boundary used to strain](#where-the-file-boundary-used-to-strain)
|
||||||
- [The consequence in practice](#the-consequence-in-practice)
|
- [The consequence in practice](#the-consequence-in-practice)
|
||||||
<!-- /wikitool:toc -->
|
<!-- /wikitool:toc -->
|
||||||
|
|
||||||
@@ -92,36 +92,50 @@ also why `ENVIRONMENT.md` only warrants a WARN rather than a FAIL when absent -
|
|||||||
checkout among possibly several and is gitignored for that reason, so its absence is a normal
|
checkout among possibly several and is gitignored for that reason, so its absence is a normal
|
||||||
state rather than a sign setup was skipped.
|
state rather than a sign setup was skipped.
|
||||||
|
|
||||||
## Where the file boundary strains
|
## Where the file boundary used to strain
|
||||||
|
|
||||||
"The file itself already answers that" holds for every file above except one shape, and it is
|
"The file itself already answers that" held for every file above except one shape: a `root: kb`
|
||||||
worth naming rather than discovering: a `root: kb` type-spec has two audiences inside one file.
|
type-spec used to carry two audiences inside one file.
|
||||||
|
|
||||||
Its authoring guidance - when to use this type, what each frontmatter field means, how to cite -
|
Its authoring guidance - when to use this type, what each frontmatter field means, how to cite -
|
||||||
is instruction to an agent. It reads like the stack's own prose because it *is* the stack's own
|
was instruction to an agent. It read like the stack's own prose because it *was* the stack's own
|
||||||
prose: a later release that learns something about writing entity pages would want to improve it
|
prose: a later release that learned something about writing entity pages would want to improve it
|
||||||
everywhere. Its `## Template` block and its `layout:` titles are the opposite: they become the
|
everywhere. Its `## Template` block and its `layout:` titles were the opposite: they became the
|
||||||
literal headings of pages this instance writes, in the language this instance chose, and no
|
literal headings of pages this instance writes, in the language this instance chose, and no
|
||||||
release has any business touching them.
|
release had any business touching them.
|
||||||
|
|
||||||
The same file is where the language question comes apart from this one, and for the same reason:
|
The same file is where the language question comes apart from the ownership one, and for the same
|
||||||
ownership decides who may change a line, its reader decides what language it is in - which is why
|
reason: ownership decides who may change a line, its reader decides what language it is in -
|
||||||
an instance's own type-spec keeps English guidance around a template block in its own language.
|
which is why a `root: kb` type-spec still keeps English prose around a template block written in
|
||||||
[language-boundaries.md](language-boundaries.md) has that argument; the rest of this page is about
|
its own language. [language-boundaries.md](language-boundaries.md) has that argument; this page is
|
||||||
ownership alone.
|
about ownership alone.
|
||||||
|
|
||||||
Ownership is per file, so the instance keeps both halves. The template half is correct that way.
|
Ownership is per file, so a file carrying both audiences had to give both halves to whoever owned
|
||||||
The guidance half pays for it: an instance that adopted its type-specs at setup will never
|
it. The template half was correct that way. The guidance half paid for it: an instance that
|
||||||
receive an improvement to them again, because `dist upgrade` writes the `.template` beside the
|
adopted its type-specs at setup never received an improvement to the guidance again, because
|
||||||
adopted file and never the file itself. Nothing breaks, and nothing reports it - the instance
|
`dist upgrade` wrote the `.template` beside the adopted file and never the file itself. Nothing
|
||||||
simply keeps reading the guidance it was handed the day it was created.
|
broke, and nothing reported it - the instance simply kept reading the guidance it was handed the
|
||||||
|
day it was created.
|
||||||
|
|
||||||
This is not an argument against the per-file boundary; the boundary is what makes an upgrade
|
That was not an argument against the per-file boundary; the boundary is what makes an upgrade
|
||||||
safe at all, and merging inside a shared file is the failure the whole section above is about.
|
safe at all, and merging inside a shared file is the failure the whole section above is about. It
|
||||||
It is an argument that this particular file is cut in the wrong place. Splitting it - the
|
was an argument that this particular file was cut in the wrong place - so it was cut again. A
|
||||||
template block into its own instance-owned file, the guidance staying stack-owned and shipping
|
`root: kb` type-spec may now declare `guidance:`, a repo-relative path to a second,
|
||||||
verbatim - would put the boundary back where the audiences actually divide, and would cost a
|
stack-owned file (`types/<name>.guidance.md`) holding exactly the half that used to be stranded:
|
||||||
migration for every instance that already adopted one.
|
when to use the type, when not to, and mechanism-level advice that holds for every instance. That
|
||||||
|
file ships verbatim and upgrades like any other machinery file, whether or not the type-spec that
|
||||||
|
links it has ever been adopted. `types/type-spec.md` §§ "Who owns a type-spec" and "Anatomy of a
|
||||||
|
type" hold the current shape; `tools/wikitool types describe <name>` composes both files into one
|
||||||
|
answer, so an agent asking for a type's contract never needs to know it comes from more than one
|
||||||
|
file. An instance that adopted its type-specs before this split existed takes it as an *offered*
|
||||||
|
migration rather than something an upgrade applies on its own - the same reasoning as any other
|
||||||
|
instance-owned file in the middle category below, spelled out for this one case because it is the
|
||||||
|
case that motivated the category existing at all.
|
||||||
|
|
||||||
|
A type-spec that declares no `guidance:` - one an instance writes entirely for itself - is
|
||||||
|
unaffected: it is still described from its own body alone, the way every type-spec worked before
|
||||||
|
`guidance:` existed. The split is optional exactly where there is no stack-owned improvement to
|
||||||
|
receive.
|
||||||
|
|
||||||
## The consequence in practice
|
## The consequence in practice
|
||||||
|
|
||||||
@@ -129,7 +143,9 @@ An upgrade sorts every shipped path into three categories, not two - and the thi
|
|||||||
becomes visible once an upgrade is a command rather than a hand-run copy:
|
becomes visible once an upgrade is a command rather than a hand-run copy:
|
||||||
|
|
||||||
- **Verbatim files** - `AGENTS.md`, `kb/CONTRACT.md`, the per-stage contracts, everything under
|
- **Verbatim files** - `AGENTS.md`, `kb/CONTRACT.md`, the per-stage contracts, everything under
|
||||||
`tools/`, `types/` and `instructions/` - are the release's to replace.
|
`tools/`, `types/` and `instructions/` - are the release's to replace. This is where a `root:
|
||||||
|
kb` type-spec's optional `types/<name>.guidance.md` sits: verbatim, even though the type-spec
|
||||||
|
it documents (below) is not.
|
||||||
- **`.template`-sourced files** - `USER.md`, `SOUL.md`, `kb/CONVENTIONS.md`, each
|
- **`.template`-sourced files** - `USER.md`, `SOUL.md`, `kb/CONVENTIONS.md`, each
|
||||||
`kb/<name>/COLLECTION.md`, `ENVIRONMENT.md`, the `root: kb` type-specs - are never written by
|
`kb/<name>/COLLECTION.md`, `ENVIRONMENT.md`, the `root: kb` type-specs - are never written by
|
||||||
an upgrade at all. The distribution ships only the `.template` beside them, so the filled file
|
an upgrade at all. The distribution ships only the `.template` beside them, so the filled file
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
---
|
||||||
|
type: types/instruction.md
|
||||||
|
name: 6.0.0-type-guidance-split
|
||||||
|
description: "Add a guidance: field to an adopted root:kb type-spec so it starts receiving the stack's authoring-prose improvements again, without touching the type-spec's own frontmatter or template."
|
||||||
|
manual: true
|
||||||
|
migrates_to: 6.0.0
|
||||||
|
migration_kind: assisted
|
||||||
|
obligation: offered
|
||||||
|
---
|
||||||
|
# Link an adopted type-spec to its stack-owned guidance file (6.0.0)
|
||||||
|
|
||||||
|
Before 6.0.0, a `root: kb` type-spec (`entity`, `concept`, `source`, `comparison`, or one this
|
||||||
|
instance added itself) carried its generic authoring prose - when to use the type, when not to,
|
||||||
|
mechanism-level advice such as citation and provenance rules - in the same file as its frontmatter
|
||||||
|
configuration and its `## Template` block. Adopting the type-spec at setup meant adopting all of
|
||||||
|
it at once, and an upgrade never touched the adopted file again: the prose an instance received
|
||||||
|
was frozen at the day it ran `setup-instance.md`, while every later improvement shipped only in
|
||||||
|
the `.template` beside it (`docs/ownership-and-templates.md` § "Where the file boundary used to
|
||||||
|
strain").
|
||||||
|
|
||||||
|
6.0.0 splits that prose into a separate, stack-owned `types/<name>.guidance.md`, linked from the
|
||||||
|
type-spec via an optional `guidance:` frontmatter field. The new file ships verbatim and upgrades
|
||||||
|
like any other machinery file from here on - but only once a type-spec actually points at it.
|
||||||
|
Taking this offer is exactly that: adding one frontmatter line per adopted type-spec. It is
|
||||||
|
`assisted`, not `mechanical`, because whether this instance's own copy of the prose has diverged
|
||||||
|
from the shipped default is a judgment call a script cannot make.
|
||||||
|
|
||||||
|
This migration is **offered, not required**. A type-spec with no `guidance:` keeps working
|
||||||
|
exactly as it did before 6.0.0 - it is described from its own body alone. Declining costs nothing
|
||||||
|
except future improvements to the prose half; nothing about the machinery stops fitting.
|
||||||
|
|
||||||
|
<!-- wikitool:toc -->
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
- [When to run](#when-to-run)
|
||||||
|
- [Steps](#steps)
|
||||||
|
- [How to tell a migrated type-spec from an unmigrated one](#how-to-tell-a-migrated-type-spec-from-an-unmigrated-one)
|
||||||
|
- [Decision points](#decision-points)
|
||||||
|
- [Scope](#scope)
|
||||||
|
<!-- /wikitool:toc -->
|
||||||
|
|
||||||
|
## When to run
|
||||||
|
|
||||||
|
Any time after installing 6.0.0 machinery over an instance that adopted at least one `root: kb`
|
||||||
|
type-spec before this migration existed. `tools/wikitool migrate status` lists it under "optional
|
||||||
|
upgrade(s) available"; taking it is not gated on anything else being current.
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. **Confirm the new guidance files actually arrived.** `dist upgrade` writes `types/<name>.guidance.md`
|
||||||
|
as an ordinary new/unchanged file - it does not depend on this migration at all. If
|
||||||
|
`ls types/*.guidance.md` shows nothing, the machinery upgrade has not landed yet; run that
|
||||||
|
first.
|
||||||
|
|
||||||
|
2. **For each adopted `root: kb` type-spec, decide whether its authoring prose still matches the
|
||||||
|
shipped default.** Compare the type-spec's current prose (everything outside `## Frontmatter`
|
||||||
|
and `## Template`) against the corresponding `types/<name>.guidance.md`:
|
||||||
|
|
||||||
|
- **Unchanged, or changed only in ways this instance is happy to lose:** proceed to step 3
|
||||||
|
directly - the new guidance file already carries the improved version.
|
||||||
|
- **Locally edited in a way worth keeping** (a house style note, an extra rule specific to
|
||||||
|
this corpus): that edit has to move somewhere before the old prose is dropped. Either fold
|
||||||
|
it into a local copy of the guidance file this instance then owns for itself (any path is
|
||||||
|
valid for `guidance:`, not only the shipped one), or keep it in the type-spec's own body
|
||||||
|
instead of adding `guidance:` at all - both are legitimate; declining the stack default for
|
||||||
|
one type is not an error.
|
||||||
|
|
||||||
|
3. **Add `guidance: types/<name>.guidance.md` to the type-spec's frontmatter** - by hand, the same
|
||||||
|
way any other type-spec frontmatter field is written (a type-spec is machinery, not a `kb/`
|
||||||
|
page, so this is not a `wikitool touch` call). Do not remove `## Frontmatter` or `## Template`;
|
||||||
|
only the generic prose around them is what the guidance file now carries.
|
||||||
|
|
||||||
|
4. **Delete the now-duplicated prose from the type-spec**, keeping the H1, a short pointer to the
|
||||||
|
guidance file (`types/entity.md`'s own current text is the worked example), `## Frontmatter`
|
||||||
|
and `## Template`. Where step 2 found a local edit worth keeping and it lives in the
|
||||||
|
type-spec's own body rather than a private guidance file, leave that part exactly where it is.
|
||||||
|
|
||||||
|
5. **Verify:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tools/wikitool types describe <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
The output must read the same as it did before this migration - the guidance prose composed
|
||||||
|
ahead of the type-spec's own body, in one answer. A diff against the pre-migration output of
|
||||||
|
the same command, restricted to wording, is expected only where step 2 found something to
|
||||||
|
drop or fold in; the structure (frontmatter fields, template block) must be byte-identical.
|
||||||
|
|
||||||
|
6. **Record it:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tools/wikitool migrate done 6.0.0 --pages 0
|
||||||
|
```
|
||||||
|
|
||||||
|
`--pages 0` because no `kb/` page changes - this migration touches machinery under `types/`
|
||||||
|
only. This does **not** advance `kb_version`, per `obligation: offered` above; it only marks
|
||||||
|
the offer as taken so `migrate status` stops listing it.
|
||||||
|
|
||||||
|
## How to tell a migrated type-spec from an unmigrated one
|
||||||
|
|
||||||
|
`grep -L '^guidance:' types/*.md` (excluding `.guidance.md` files themselves, which never carry
|
||||||
|
the field) lists every `root: kb` type-spec that has not taken the offer yet.
|
||||||
|
|
||||||
|
## Decision points
|
||||||
|
|
||||||
|
- **A type this instance wrote entirely for itself?** No `types/<name>.guidance.md` exists for
|
||||||
|
it and none should be authored to match this migration artificially - `guidance:` is for
|
||||||
|
receiving a *stack* default, and a self-written type has none to receive. Leave it as it is.
|
||||||
|
- **Local prose worth keeping, but no interest in maintaining a private guidance file?** Skip
|
||||||
|
`guidance:` for that one type-spec. Nothing forces uniformity across an instance's own types.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
For `types/` machinery, not `kb/` content - the one migration document in this directory that
|
||||||
|
is. No page's frontmatter or body changes, `sources coverage`/`lint`/`kb_version` are all
|
||||||
|
unaffected, and `migrate done`'s `--pages` is `0` for exactly that reason.
|
||||||
@@ -89,9 +89,16 @@ and ready for its first ingest.
|
|||||||
|
|
||||||
Under `types/` this covers exactly the type-specs with `root: kb` - `entity`, `concept`,
|
Under `types/` this covers exactly the type-specs with `root: kb` - `entity`, `concept`,
|
||||||
`source`, `comparison` - along with their `.schema.yaml`. They describe pages *this*
|
`source`, `comparison` - along with their `.schema.yaml`. They describe pages *this*
|
||||||
instance writes, so they belong to it: prose, template and language may all be rewritten.
|
instance writes, so they belong to it: frontmatter, template and language may all be
|
||||||
`instruction`, `lint-report` and `type-spec` describe stack artifacts and arrive
|
rewritten. `instruction`, `lint-report`, `type-spec` and `type-guidance` describe stack
|
||||||
unchanged.
|
artifacts and arrive unchanged - the glob above never matches them because none of them
|
||||||
|
ships as a `.template` in the first place.
|
||||||
|
|
||||||
|
A `root: kb` type-spec's generic authoring guidance (when to use the type, when not to)
|
||||||
|
is not part of this adoption at all: it lives in a sibling `types/<name>.guidance.md`
|
||||||
|
this instance never renames, the same as `instruction.md` - it ships verbatim and a later
|
||||||
|
`dist upgrade` improves it directly, without the type-spec that links it needing to be
|
||||||
|
touched. `types/type-spec.md` § "Anatomy of a type" has the shape.
|
||||||
|
|
||||||
2. Ask the user for the KB language. `kb/CONVENTIONS.md.template` defaults to **English**;
|
2. Ask the user for the KB language. `kb/CONVENTIONS.md.template` defaults to **English**;
|
||||||
[kb-profiles.md](kb-profiles.md) additionally holds a complete German profile, whose full
|
[kb-profiles.md](kb-profiles.md) additionally holds a complete German profile, whose full
|
||||||
|
|||||||
+1
-1
@@ -166,7 +166,7 @@ tools/wikitool <command> --help
|
|||||||
| Command | Purpose |
|
| Command | Purpose |
|
||||||
|---------|---------|
|
|---------|---------|
|
||||||
| `types list [--json]` | List every type-spec under `types/` (name, schema path, subtype field, description) - discover what page types exist without reading `types/*.md` directly |
|
| `types list [--json]` | List every type-spec under `types/` (name, schema path, subtype field, description) - discover what page types exist without reading `types/*.md` directly |
|
||||||
| `types describe <name> [--json]` | Print one type's full contract: required/optional frontmatter fields with enums, its subtype field (if any), and its authoring body. A type-spec over the `docs toc` threshold carries a generated table-of-contents region; it is stripped from this output rather than echoed, since the whole body is being handed over and a navigation aid into it would be noise |
|
| `types describe <name> [--json]` | Print one type's full contract: required/optional frontmatter fields with enums, its subtype field (if any), and its authoring body - composed with the stack-owned `types/<name>.guidance.md` where the type-spec declares `guidance:` (`--json` reports it separately as `guidance`/`guidance_path`, absent for a type with none), so a `root: kb` type's contract reads as one answer even though it may live in two files. A type-spec (or its guidance file) over the `docs toc` threshold carries a generated table-of-contents region; it is stripped from this output rather than echoed, since the whole body is being handed over and a navigation aid into it would be noise |
|
||||||
| `instructions sync [--force]` | Publish every `instructions/<name>/SKILL.md` into `.agents/skills/` and `.claude/skills/` as **copies**, and delete published skills whose source is gone. Both targets are gitignored, so a fresh clone runs this once - see `instructions/bootstrap.md`. Re-running is also how a drifted copy is repaired: the source always wins. `--force` is required only to replace a target directory that is not a published skill at all (no `SKILL.md` in it) |
|
| `instructions sync [--force]` | Publish every `instructions/<name>/SKILL.md` into `.agents/skills/` and `.claude/skills/` as **copies**, and delete published skills whose source is gone. Both targets are gitignored, so a fresh clone runs this once - see `instructions/bootstrap.md`. Re-running is also how a drifted copy is repaired: the source always wins. `--force` is required only to replace a target directory that is not a published skill at all (no `SKILL.md` in it) |
|
||||||
| `instructions verify` | Check the instruction layer: flat instructions validate against `types/instruction.schema.yaml`, each `SKILL.md` carries the frontmatter its harness reads, no `SKILL.md` carries a relative markdown link (`sync` copies it to a different depth than the source, so a `SKILL.md` references a target as a repo-root-relative plain path instead - see [instructions/CONTRACT.md](../instructions/CONTRACT.md) § "A skill's outbound reference is a plain path, not a link"), every published copy is byte-identical to its source, no instruction is left that nothing references, and nothing under `instructions/dev/` is referenced from outside it (a `<!-- dist:strip-start/end -->` block is exempt - see [instructions/CONTRACT.md](../instructions/CONTRACT.md)). Missing *every* copy is reported as "run sync", not as drift - that is a clean checkout |
|
| `instructions verify` | Check the instruction layer: flat instructions validate against `types/instruction.schema.yaml`, each `SKILL.md` carries the frontmatter its harness reads, no `SKILL.md` carries a relative markdown link (`sync` copies it to a different depth than the source, so a `SKILL.md` references a target as a repo-root-relative plain path instead - see [instructions/CONTRACT.md](../instructions/CONTRACT.md) § "A skill's outbound reference is a plain path, not a link"), every published copy is byte-identical to its source, no instruction is left that nothing references, and nothing under `instructions/dev/` is referenced from outside it (a `<!-- dist:strip-start/end -->` block is exempt - see [instructions/CONTRACT.md](../instructions/CONTRACT.md)). Missing *every* copy is reported as "run sync", not as drift - that is a clean checkout |
|
||||||
| `instructions list [--json]` | List the flat instructions with their descriptions. This is how the layer is discovered; `search` deliberately covers `kb/` only |
|
| `instructions list [--json]` | List the flat instructions with their descriptions. This is how the layer is discovered; `search` deliberately covers `kb/` only |
|
||||||
|
|||||||
@@ -338,6 +338,32 @@ def instance_owned_type_stems() -> set[str]:
|
|||||||
return stems
|
return stems
|
||||||
|
|
||||||
|
|
||||||
|
# The suffix a type-spec's own two files carry - `<stem>.md` and
|
||||||
|
# `<stem>.schema.yaml` - as opposed to a sibling file that merely starts with
|
||||||
|
# the same stem, such as `<stem>.guidance.md` (Gitea #104). Checked as an
|
||||||
|
# exact suffix rather than by splitting on the first `.`, which is what let
|
||||||
|
# `entity.guidance.md` be mistaken for the `entity` type-spec's own file
|
||||||
|
# before this existed - a stack-owned file re-keyed as though it were the
|
||||||
|
# instance's `.template` to adopt, and flagged as a leak by the other call
|
||||||
|
# site for not being one.
|
||||||
|
_TYPE_SCHEMA_SUFFIX = ".schema.yaml"
|
||||||
|
|
||||||
|
|
||||||
|
def _owned_type_stem(relative: str) -> Optional[str]:
|
||||||
|
"""The type stem `relative` (a path under `types/`, no `.template`
|
||||||
|
suffix) names, if it is exactly that type-spec's own `<stem>.md` or
|
||||||
|
`<stem>.schema.yaml` - `None` for anything else under `types/`,
|
||||||
|
including a `<stem>.guidance.md` file. `_plan_types()` and `find_leaks()`
|
||||||
|
both ask this instead of computing their own stem, so the two answer the
|
||||||
|
same question about the same path (AGENTS.md invariant 8)."""
|
||||||
|
name = relative.rsplit("/", 1)[-1]
|
||||||
|
if name.endswith(_TYPE_SCHEMA_SUFFIX):
|
||||||
|
return name[: -len(_TYPE_SCHEMA_SUFFIX)]
|
||||||
|
if name.endswith(".md") and not name.endswith(".guidance.md"):
|
||||||
|
return name[: -len(".md")]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _plan_types() -> dict[str, PlannedFile]:
|
def _plan_types() -> dict[str, PlannedFile]:
|
||||||
"""`types/`, with the page type-specs re-keyed as templates.
|
"""`types/`, with the page type-specs re-keyed as templates.
|
||||||
|
|
||||||
@@ -347,6 +373,11 @@ def _plan_types() -> dict[str, PlannedFile]:
|
|||||||
to be adopted before it counts. A type-spec's `.schema.yaml` travels with
|
to be adopted before it counts. A type-spec's `.schema.yaml` travels with
|
||||||
it, because the two are one type (see types/type-spec.md § Anatomy) and
|
it, because the two are one type (see types/type-spec.md § Anatomy) and
|
||||||
adopting half of it would leave a spec validated by a file it does not own.
|
adopting half of it would leave a spec validated by a file it does not own.
|
||||||
|
|
||||||
|
A type-spec's optional `<name>.guidance.md` (Gitea #104) is the opposite:
|
||||||
|
stack-owned even where the type-spec itself is instance-owned, and ships
|
||||||
|
verbatim beside the `.template` - `_owned_type_stem` is what keeps it out
|
||||||
|
of this re-keying despite sharing the type-spec's own stem.
|
||||||
"""
|
"""
|
||||||
plan = _copy_tree(config.TYPES_DIR, "types", frozenset())
|
plan = _copy_tree(config.TYPES_DIR, "types", frozenset())
|
||||||
stems = instance_owned_type_stems()
|
stems = instance_owned_type_stems()
|
||||||
@@ -355,9 +386,8 @@ def _plan_types() -> dict[str, PlannedFile]:
|
|||||||
|
|
||||||
rekeyed: dict[str, PlannedFile] = {}
|
rekeyed: dict[str, PlannedFile] = {}
|
||||||
for relative, planned in plan.items():
|
for relative, planned in plan.items():
|
||||||
name = relative.rsplit("/", 1)[-1]
|
stem = _owned_type_stem(relative)
|
||||||
stem = name.split(".", 1)[0]
|
if stem is not None and stem in stems:
|
||||||
if stem in stems:
|
|
||||||
rekeyed[f"{relative}.template"] = planned
|
rekeyed[f"{relative}.template"] = planned
|
||||||
else:
|
else:
|
||||||
rekeyed[relative] = planned
|
rekeyed[relative] = planned
|
||||||
@@ -512,7 +542,8 @@ def find_leaks(plan: dict[str, PlannedFile]) -> list[str]:
|
|||||||
elif (
|
elif (
|
||||||
relative.startswith("types/")
|
relative.startswith("types/")
|
||||||
and not relative.endswith(".template")
|
and not relative.endswith(".template")
|
||||||
and name.split(".", 1)[0] in owned_types
|
and (owned_stem := _owned_type_stem(relative)) is not None
|
||||||
|
and owned_stem in owned_types
|
||||||
):
|
):
|
||||||
leaks.append(f"{relative} (this instance's page type-spec; ship the .template)")
|
leaks.append(f"{relative} (this instance's page type-spec; ship the .template)")
|
||||||
elif relative.startswith("instructions/dev/"):
|
elif relative.startswith("instructions/dev/"):
|
||||||
|
|||||||
@@ -55,7 +55,10 @@ def describe_type_command(
|
|||||||
"""Print one type's full contract: frontmatter fields (required/optional,
|
"""Print one type's full contract: frontmatter fields (required/optional,
|
||||||
with enums where declared), its subtype field if any, and its authoring
|
with enums where declared), its subtype field if any, and its authoring
|
||||||
body - the same information an LLM would otherwise gather by reading the
|
body - the same information an LLM would otherwise gather by reading the
|
||||||
raw type-spec and `.schema.yaml` files directly."""
|
raw type-spec and `.schema.yaml` files directly. Where the type-spec
|
||||||
|
declares `guidance:`, that stack-owned file's prose is composed in ahead
|
||||||
|
of the type-spec's own body, so a `root: kb` type's contract still reads
|
||||||
|
as one answer even though it lives in two files (Gitea #104)."""
|
||||||
try:
|
try:
|
||||||
described = describe_type(name)
|
described = describe_type(name)
|
||||||
except UnknownType as exc:
|
except UnknownType as exc:
|
||||||
@@ -89,9 +92,13 @@ def describe_type_command(
|
|||||||
typer.echo("")
|
typer.echo("")
|
||||||
|
|
||||||
typer.echo("## Authoring guidance")
|
typer.echo("## Authoring guidance")
|
||||||
# A type-spec over 100 lines carries a generated table-of-contents region
|
# A type-spec (and its guidance file) over 100 lines carries a generated
|
||||||
# (`chemenu/toc.py`), which serves whoever opens the file. Here it would be
|
# table-of-contents region (`chemenu/toc.py`), which serves whoever opens
|
||||||
# noise: this command already hands over the whole body, so there is
|
# the file directly. Here it would be noise: this command already hands
|
||||||
# nothing left for a navigation aid to navigate - only markers and a list
|
# over the whole body, so there is nothing left for a navigation aid to
|
||||||
# of headings the reader is about to see anyway.
|
# navigate - only markers and a list of headings the reader is about to
|
||||||
|
# see anyway.
|
||||||
|
if described["guidance"]:
|
||||||
|
typer.echo(toc.strip_region(described["guidance"]))
|
||||||
|
typer.echo("")
|
||||||
typer.echo(toc.strip_region(described["body"]))
|
typer.echo(toc.strip_region(described["body"]))
|
||||||
|
|||||||
@@ -81,7 +81,16 @@ def repo(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
|
|||||||
# the instance's; `instruction` writes into the repo and is the stack's.
|
# the instance's; `instruction` writes into the repo and is the stack's.
|
||||||
(types_dir / "entity.md").write_text(
|
(types_dir / "entity.md").write_text(
|
||||||
"---\ntype: types/type-spec.md\nname: entity\ndescription: d\n"
|
"---\ntype: types/type-spec.md\nname: entity\ndescription: d\n"
|
||||||
"schema: types/entity.schema.yaml\nbase_dir: entities\n---\n\n# Entity\n",
|
"schema: types/entity.schema.yaml\nbase_dir: entities\n"
|
||||||
|
"guidance: types/entity.guidance.md\n---\n\n# Entity\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
# entity's guidance: stack-owned even though entity.md itself is
|
||||||
|
# instance-owned - the file `_owned_type_stem` must not mistake for
|
||||||
|
# entity's own `.md`/`.schema.yaml` despite sharing its stem (Gitea #104).
|
||||||
|
(types_dir / "entity.guidance.md").write_text(
|
||||||
|
"---\ntype: types/type-guidance.md\nname: entity\ndescription: When to use entity.\n"
|
||||||
|
"---\n\n# Entity Guidance\n",
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
(types_dir / "instruction.md").write_text(
|
(types_dir / "instruction.md").write_text(
|
||||||
@@ -394,6 +403,20 @@ def test_page_type_specs_ship_as_templates_and_stack_types_do_not(repo, monkeypa
|
|||||||
assert "types/instruction.md.template" not in plan
|
assert "types/instruction.md.template" not in plan
|
||||||
|
|
||||||
|
|
||||||
|
def test_guidance_file_ships_verbatim_beside_a_templated_type_spec(repo, monkeypatch):
|
||||||
|
"""Gitea #104: `types/entity.guidance.md` is stack-owned even though
|
||||||
|
`types/entity.md` (same stem) is instance-owned - it must cross like
|
||||||
|
`types/instruction.md` above, never re-keyed as though it were the
|
||||||
|
type-spec's own `.template`."""
|
||||||
|
from chemenu.type_resolver import resolver
|
||||||
|
|
||||||
|
monkeypatch.setattr(resolver, "_repo_root", config.ROOT)
|
||||||
|
plan = dist_cmd.build_plan()
|
||||||
|
|
||||||
|
assert "types/entity.guidance.md" in plan
|
||||||
|
assert "types/entity.guidance.md.template" not in plan
|
||||||
|
|
||||||
|
|
||||||
def test_plan_creates_empty_raw_and_incoming_not_real_content(repo):
|
def test_plan_creates_empty_raw_and_incoming_not_real_content(repo):
|
||||||
"""Both flat since Gitea #67: `raw/` addresses a file by its accept date,
|
"""Both flat since Gitea #67: `raw/` addresses a file by its accept date,
|
||||||
never by a hand-picked type, so there is nothing left to seed per type."""
|
never by a hand-picked type, so there is nothing left to seed per type."""
|
||||||
|
|||||||
@@ -223,6 +223,56 @@ def test_seeded_once_paths_are_never_written_even_if_the_release_stamp_lists_the
|
|||||||
assert not (instance / preserved).exists()
|
assert not (instance / preserved).exists()
|
||||||
|
|
||||||
|
|
||||||
|
# --- a root:kb type-spec's guidance half upgrades like any other file -------
|
||||||
|
#
|
||||||
|
# Gitea #104: before the split, `types/<name>.md` carried both the
|
||||||
|
# instance-owned template and the stack-owned authoring prose in one file, so
|
||||||
|
# an instance that had adopted it (renamed the `.template`) never received a
|
||||||
|
# prose improvement again - `dist upgrade` only ever wrote the `.template`
|
||||||
|
# beside the adopted file, never the file itself. Splitting the prose into a
|
||||||
|
# sibling `.guidance.md` that ships verbatim (never `.template`-sourced) means
|
||||||
|
# it upgrades through the ordinary unchanged/new path below, even though the
|
||||||
|
# type-spec it documents is never in the stamp at all and therefore never
|
||||||
|
# touched.
|
||||||
|
|
||||||
|
|
||||||
|
def test_upgrade_writes_improved_guidance_prose_over_an_adopted_type_spec(instance, tmp_path):
|
||||||
|
(instance / "types").mkdir()
|
||||||
|
(instance / "types" / "entity.md").write_text(
|
||||||
|
# Adopted from `types/entity.md.template` at some earlier setup - this
|
||||||
|
# file was never part of any release stamp and `dist upgrade` must
|
||||||
|
# never touch it.
|
||||||
|
"---\ntype: types/type-spec.md\nname: entity\ndescription: d\n"
|
||||||
|
"schema: types/entity.schema.yaml\nbase_dir: entities\n"
|
||||||
|
"guidance: types/entity.guidance.md\n---\n\n# Entity\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
(instance / "types" / "entity.guidance.md").write_text("old guidance\n", encoding="utf-8")
|
||||||
|
stamp = json.loads((instance / version_mod.RELEASE_STAMP_FILENAME).read_text())
|
||||||
|
stamp["files"]["types/entity.guidance.md"] = _digest("old guidance\n")
|
||||||
|
(instance / version_mod.RELEASE_STAMP_FILENAME).write_text(json.dumps(stamp), encoding="utf-8")
|
||||||
|
|
||||||
|
release = _release(
|
||||||
|
tmp_path, "release", "1.1.0",
|
||||||
|
{
|
||||||
|
"AGENTS.md": "core\n",
|
||||||
|
"tools/wikitool": "#!/bin/sh\n",
|
||||||
|
"types/entity.guidance.md": "improved guidance\n",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
dist_cmd.run_upgrade(release)
|
||||||
|
|
||||||
|
assert (instance / "types" / "entity.guidance.md").read_text(encoding="utf-8") == (
|
||||||
|
"improved guidance\n"
|
||||||
|
)
|
||||||
|
# The adopted type-spec itself was never in either stamp, so it is
|
||||||
|
# completely untouched by the upgrade.
|
||||||
|
assert "guidance: types/entity.guidance.md" in (
|
||||||
|
instance / "types" / "entity.md"
|
||||||
|
).read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
# --- migration chain: reported, never run -----------------------------------
|
# --- migration chain: reported, never run -----------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,54 @@ def test_get_page_ref_fields_defaults_to_empty():
|
|||||||
assert resolver.get_page_ref_fields("types/type-spec.md") == []
|
assert resolver.get_page_ref_fields("types/type-spec.md") == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_guidance_reads_the_linked_type_guidance_file():
|
||||||
|
"""Gitea #104: a root:kb type-spec's generic authoring prose lives in a
|
||||||
|
separate, stack-owned `<name>.guidance.md`, linked via `guidance:` -
|
||||||
|
unlike `schema:`, this one's absence is the common case (an instance
|
||||||
|
typed for itself), not an error."""
|
||||||
|
guidance = resolver.get_guidance("types/entity.md")
|
||||||
|
assert guidance is not None
|
||||||
|
assert "When to use" in guidance or "When NOT to use" in guidance
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_guidance_is_none_when_the_type_spec_declares_none():
|
||||||
|
"""`instruction` and `lint-report` describe stack artifacts and have
|
||||||
|
never carried a `guidance:` field - this is the type with no linked
|
||||||
|
guidance at all, not a broken link."""
|
||||||
|
assert resolver.get_guidance("types/instruction.md") is None
|
||||||
|
assert resolver.get_guidance("types/lint-report.md") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_extract_template_reads_only_the_type_spec_never_the_guidance_file(tmp_path):
|
||||||
|
"""`wikitool new` must keep exactly one load path for its scaffold - the
|
||||||
|
first ```markdown block of `types/<name>.md` itself - even though the
|
||||||
|
type-spec now optionally links a second file. A ```markdown block placed
|
||||||
|
in the guidance file instead must never be picked up."""
|
||||||
|
from chemenu.type_resolver import TypeResolver
|
||||||
|
|
||||||
|
types_dir = tmp_path / "types"
|
||||||
|
types_dir.mkdir()
|
||||||
|
(types_dir / "widget.md").write_text(
|
||||||
|
# Self-referential `type:` (like the badtype fixture above), purely so
|
||||||
|
# this narrow fixture needs no real `types/type-spec.md` on disk -
|
||||||
|
# `extract_template` never reads the `type:` field at all.
|
||||||
|
"---\ntype: types/widget.md\nname: widget\ndescription: A widget type.\n"
|
||||||
|
"schema: null\nbase_dir: widgets\nguidance: types/widget.guidance.md\n---\n\n"
|
||||||
|
"# Widget\n\n## Template\n\n```markdown\n# {name}\n```\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
(types_dir / "widget.guidance.md").write_text(
|
||||||
|
"---\ntype: types/type-guidance.md\nname: widget\ndescription: Guidance for widget.\n"
|
||||||
|
"---\n\n# Widget Guidance\n\n```markdown\nTHIS MUST NEVER BE THE SCAFFOLD\n```\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
bad_resolver = TypeResolver(repo_root=tmp_path)
|
||||||
|
type_spec = bad_resolver.load_type_spec("types/widget.md")
|
||||||
|
template = bad_resolver.extract_template(type_spec)
|
||||||
|
assert template == "# {name}"
|
||||||
|
assert "THIS MUST NEVER BE THE SCAFFOLD" not in template
|
||||||
|
|
||||||
|
|
||||||
def test_get_capture_fields_reads_the_type_spec():
|
def test_get_capture_fields_reads_the_type_spec():
|
||||||
"""`fidelity`/`authority` are fixed once, at capture time (Gitea #67) -
|
"""`fidelity`/`authority` are fixed once, at capture time (Gitea #67) -
|
||||||
`raw accept`, `new source` and `touch` all read the field list from here
|
`raw accept`, `new source` and `touch` all read the field list from here
|
||||||
@@ -221,6 +269,7 @@ def test_list_type_specs_finds_every_type_spec():
|
|||||||
names = {fm.get("name") for _, fm in resolver.list_type_specs()}
|
names = {fm.get("name") for _, fm in resolver.list_type_specs()}
|
||||||
assert names == {
|
assert names == {
|
||||||
"type-spec", "entity", "concept", "source", "comparison", "lint-report", "instruction",
|
"type-spec", "entity", "concept", "source", "comparison", "lint-report", "instruction",
|
||||||
|
"type-guidance",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ def test_types_list_finds_all_current_type_specs():
|
|||||||
names = {row["name"] for row in rows}
|
names = {row["name"] for row in rows}
|
||||||
assert names == {
|
assert names == {
|
||||||
"type-spec", "entity", "concept", "source", "comparison", "lint-report", "instruction",
|
"type-spec", "entity", "concept", "source", "comparison", "lint-report", "instruction",
|
||||||
|
"type-guidance",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -52,6 +53,42 @@ def test_types_describe_entity_reports_schema_and_body():
|
|||||||
assert "wikitool:footnotes" not in data["body"]
|
assert "wikitool:footnotes" not in data["body"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_types_describe_entity_composes_guidance_and_body_separately():
|
||||||
|
"""Gitea #104: the generic authoring prose (When to use / When NOT to
|
||||||
|
use) now lives in the stack-owned `entity.guidance.md`, reported under
|
||||||
|
its own JSON keys, while `body` stays exactly what it was - the
|
||||||
|
instance-owned type-spec's own text (frontmatter table + template)."""
|
||||||
|
result = runner.invoke(app, ["types", "describe", "entity", "--json"])
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
import json
|
||||||
|
data = json.loads(result.output)
|
||||||
|
assert data["guidance_path"] == "types/entity.guidance.md"
|
||||||
|
assert data["guidance"] is not None
|
||||||
|
assert "## When to use" in data["guidance"]
|
||||||
|
assert "## When to use" not in data["body"]
|
||||||
|
assert "## Kerndaten" in data["body"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_types_describe_composes_guidance_ahead_of_body_in_text_output():
|
||||||
|
result = runner.invoke(app, ["types", "describe", "entity"])
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
guidance_at = result.output.index("## When to use")
|
||||||
|
template_at = result.output.index("## Kerndaten")
|
||||||
|
assert guidance_at < template_at
|
||||||
|
|
||||||
|
|
||||||
|
def test_types_describe_a_type_with_no_guidance_omits_it_cleanly():
|
||||||
|
"""`instruction` describes a stack artifact and has never carried a
|
||||||
|
`guidance:` field - this must read exactly as it did before the split
|
||||||
|
existed, not print an empty section."""
|
||||||
|
result = runner.invoke(app, ["types", "describe", "instruction", "--json"])
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
import json
|
||||||
|
data = json.loads(result.output)
|
||||||
|
assert data["guidance"] is None
|
||||||
|
assert data["guidance_path"] is None
|
||||||
|
|
||||||
|
|
||||||
def test_types_describe_unknown_name_fails_cleanly():
|
def test_types_describe_unknown_name_fails_cleanly():
|
||||||
result = runner.invoke(app, ["types", "describe", "bogus"])
|
result = runner.invoke(app, ["types", "describe", "bogus"])
|
||||||
assert result.exit_code != 0
|
assert result.exit_code != 0
|
||||||
|
|||||||
@@ -436,6 +436,33 @@ class TypeResolver:
|
|||||||
type_spec = self.load_type_spec(type_path, source_file)
|
type_spec = self.load_type_spec(type_path, source_file)
|
||||||
return type_spec['frontmatter'].get('title_prefix') or ""
|
return type_spec['frontmatter'].get('title_prefix') or ""
|
||||||
|
|
||||||
|
def get_guidance(self, type_path: str, source_file: Path = None) -> Optional[str]:
|
||||||
|
"""Return the stack-owned guidance body a type-spec's `guidance:`
|
||||||
|
field points to, or None if it declares none - the common case for a
|
||||||
|
type an instance writes entirely for itself (Gitea #104).
|
||||||
|
|
||||||
|
The linked file is loaded as a type-spec-shaped document (`type:
|
||||||
|
types/type-guidance.md`, `name:`, `description:`) the same way
|
||||||
|
`schema:` is resolved, so a broken link or a malformed guidance file
|
||||||
|
fails the same way a broken `schema:` would rather than silently
|
||||||
|
returning nothing.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
type_path: The type path to resolve, e.g. 'types/entity.md'
|
||||||
|
source_file: The source file path (for relative type resolution)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If the type path cannot be resolved, or `guidance:`
|
||||||
|
names a path that cannot be resolved or does not validate as
|
||||||
|
a type-guidance document.
|
||||||
|
"""
|
||||||
|
type_spec = self.load_type_spec(type_path, source_file)
|
||||||
|
guidance_path = type_spec['frontmatter'].get('guidance')
|
||||||
|
if not guidance_path:
|
||||||
|
return None
|
||||||
|
guidance_spec = self.load_type_spec(guidance_path, type_spec['path'])
|
||||||
|
return guidance_spec['body']
|
||||||
|
|
||||||
def get_page_ref_fields(self, type_path: str, source_file: Path = None) -> list:
|
def get_page_ref_fields(self, type_path: str, source_file: Path = None) -> list:
|
||||||
"""Return the frontmatter fields whose entries are wiki page titles
|
"""Return the frontmatter fields whose entries are wiki page titles
|
||||||
(e.g. `['related', 'sources']` for an entity), as declared by the
|
(e.g. `['related', 'sources']` for an entity), as declared by the
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ def describe_type(name: str) -> Dict[str, Any]:
|
|||||||
type_spec = resolver.load_type_spec(type_path)
|
type_spec = resolver.load_type_spec(type_path)
|
||||||
frontmatter = type_spec["frontmatter"]
|
frontmatter = type_spec["frontmatter"]
|
||||||
schema = resolver.get_schema(type_path)
|
schema = resolver.get_schema(type_path)
|
||||||
|
guidance_path = frontmatter.get("guidance")
|
||||||
|
guidance_body = resolver.get_guidance(type_path) if guidance_path else None
|
||||||
|
|
||||||
fields: list[Dict[str, Any]] = []
|
fields: list[Dict[str, Any]] = []
|
||||||
if schema is not None:
|
if schema is not None:
|
||||||
@@ -86,5 +88,10 @@ def describe_type(name: str) -> Dict[str, Any]:
|
|||||||
"base_dir": frontmatter.get("base_dir"),
|
"base_dir": frontmatter.get("base_dir"),
|
||||||
"title_prefix": frontmatter.get("title_prefix"),
|
"title_prefix": frontmatter.get("title_prefix"),
|
||||||
"fields": fields,
|
"fields": fields,
|
||||||
|
# `body` stays the type-spec's own body, unchanged - additive fields
|
||||||
|
# below it keep the MCP wire contract readable for an older client
|
||||||
|
# that has never heard of the guidance split (Gitea #104).
|
||||||
"body": type_spec["body"].strip(),
|
"body": type_spec["body"].strip(),
|
||||||
|
"guidance_path": guidance_path,
|
||||||
|
"guidance": guidance_body.strip() if guidance_body is not None else None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
type: types/type-guidance.md
|
||||||
|
name: comparison
|
||||||
|
description: When to write a comparison page instead of a neighboring type, and how to write a conforming one - structure and analysis rules that hold regardless of what this instance is comparing
|
||||||
|
---
|
||||||
|
|
||||||
|
# Comparison Guidance
|
||||||
|
|
||||||
|
`comparison` is the type for pages that set several entities, tools, technologies or approaches
|
||||||
|
systematically against one another along fixed criteria. Comparison pages support decisions and
|
||||||
|
make the trade-offs between alternatives visible.
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
|
||||||
|
- Comparing several technologies or frameworks for a concrete purpose
|
||||||
|
- Analysing the advantages and disadvantages of different tools or libraries
|
||||||
|
- Evaluating alternative ways of solving a problem
|
||||||
|
- Documenting a decision process with several options examined
|
||||||
|
|
||||||
|
## When NOT to use
|
||||||
|
|
||||||
|
- For documenting a single entity or concept (use `entity` or `concept` respectively)
|
||||||
|
- For summaries of source material (use `source`)
|
||||||
|
- For pages covering a single subject rather than comparing several
|
||||||
|
|
||||||
|
## Authoring guidance
|
||||||
|
|
||||||
|
- A title that names the comparison (e.g. "Go vs Rust", "Kubernetes vs Docker Swarm"); it follows the established names of the subjects compared, not the KB language (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" and "Language")
|
||||||
|
- State clearly what is being compared and why
|
||||||
|
- Use a comparison table with the criteria as rows
|
||||||
|
- An analysis that evaluates the table rather than repeating it
|
||||||
|
- A recommendation wherever one can be justified
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Relevant Notes:
|
||||||
|
|
||||||
|
- Comparison pages support technology selection and architecture decisions
|
||||||
|
- The entities compared should all exist as wiki pages
|
||||||
|
- The criteria should fit the decision at hand
|
||||||
|
- A comparison stays focused on one concrete purpose or decision context
|
||||||
+5
-31
@@ -5,24 +5,15 @@ description: Structured type for comparison pages that set several entities or a
|
|||||||
schema: types/comparison.schema.yaml
|
schema: types/comparison.schema.yaml
|
||||||
base_dir: comparisons
|
base_dir: comparisons
|
||||||
page_ref_fields: [entities, related]
|
page_ref_fields: [entities, related]
|
||||||
|
guidance: types/comparison.guidance.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Comparison
|
# Comparison
|
||||||
|
|
||||||
`comparison` is the type for pages that set several entities, tools, technologies or approaches systematically against one another along fixed criteria. Comparison pages support decisions and make the trade-offs between alternatives visible.
|
This instance's configuration for the `comparison` type: its frontmatter fields as this schema
|
||||||
|
requires them, and the page skeleton `wikitool new comparison` scaffolds. When to use this type
|
||||||
## When to use
|
and how to write a conforming page is [types/comparison.guidance.md](comparison.guidance.md) -
|
||||||
|
`tools/wikitool types describe comparison` composes both into one answer.
|
||||||
- Comparing several technologies or frameworks for a concrete purpose
|
|
||||||
- Analysing the advantages and disadvantages of different tools or libraries
|
|
||||||
- Evaluating alternative ways of solving a problem
|
|
||||||
- Documenting a decision process with several options examined
|
|
||||||
|
|
||||||
## When NOT to use
|
|
||||||
|
|
||||||
- For documenting a single entity or concept (use `entity` or `concept` respectively)
|
|
||||||
- For summaries of source material (use `source`)
|
|
||||||
- For pages covering a single subject rather than comparing several
|
|
||||||
|
|
||||||
## Frontmatter
|
## Frontmatter
|
||||||
|
|
||||||
@@ -35,14 +26,6 @@ page_ref_fields: [entities, related]
|
|||||||
| `related` | No | Declared outbound edges - one `compares-with` edge per subject, written by `wikitool xref add` |
|
| `related` | No | Declared outbound edges - one `compares-with` edge per subject, written by `wikitool xref add` |
|
||||||
| `summary` | Yes | One-liner for `kb/index.md` |
|
| `summary` | Yes | One-liner for `kb/index.md` |
|
||||||
|
|
||||||
## Authoring guidance
|
|
||||||
|
|
||||||
- A title that names the comparison (e.g. "Go vs Rust", "Kubernetes vs Docker Swarm"); it follows the established names of the subjects compared, not the KB language (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" and "Language")
|
|
||||||
- State clearly what is being compared and why
|
|
||||||
- Use a comparison table with the criteria as rows
|
|
||||||
- An analysis that evaluates the table rather than repeating it
|
|
||||||
- A recommendation wherever one can be justified
|
|
||||||
|
|
||||||
## Template
|
## Template
|
||||||
|
|
||||||
The block below is page material, so it is written in this instance's KB language
|
The block below is page material, so it is written in this instance's KB language
|
||||||
@@ -75,12 +58,3 @@ TODO: Falls möglich - was wann und für wen zu verwenden ist. Unter welchen Ums
|
|||||||
convention, and the page title itself (`Go vs Rust`) does not carry it. When `wikitool xref` adds
|
convention, and the page title itself (`Go vs Rust`) does not carry it. When `wikitool xref` adds
|
||||||
a relationship, the tool-managed cross-reference section appears; what it is called is the
|
a relationship, the tool-managed cross-reference section appears; what it is called is the
|
||||||
instance's decision in `kb/CONVENTIONS.md` (`sections:`).
|
instance's decision in `kb/CONVENTIONS.md` (`sections:`).
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Additional notes:
|
|
||||||
|
|
||||||
- Comparison pages support technology selection and architecture decisions
|
|
||||||
- The entities compared should all exist as wiki pages
|
|
||||||
- The criteria should fit the decision at hand
|
|
||||||
- A comparison stays focused on one concrete purpose or decision context
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
type: types/type-guidance.md
|
||||||
|
name: concept
|
||||||
|
description: When to write a concept page instead of a neighboring type, and how to write a conforming one - citation, linking and register rules that hold regardless of which concept_type values this instance chose
|
||||||
|
---
|
||||||
|
|
||||||
|
# Concept Guidance
|
||||||
|
|
||||||
|
`concept` is the type for abstract ideas, methodologies, patterns and architectural decisions.
|
||||||
|
Concepts provide the theoretical and methodological ground that entities implement or follow.
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
|
||||||
|
- Documenting an architectural pattern or design approach
|
||||||
|
- Explaining a design pattern or a programming methodology
|
||||||
|
- Describing a communication protocol or standard
|
||||||
|
- Recording a workflow or process that recurs across projects
|
||||||
|
- Documenting an architectural decision (ADR) or design decision
|
||||||
|
- Analysing a recurring problem and its solutions
|
||||||
|
|
||||||
|
## When NOT to use
|
||||||
|
|
||||||
|
- For concrete things such as projects, systems or tools (use `entity`)
|
||||||
|
- For source documents or ingested material (use `source`)
|
||||||
|
- For comparative analyses of concrete subjects (use `comparison`)
|
||||||
|
|
||||||
|
## Authoring guidance
|
||||||
|
|
||||||
|
- The title is the concept's canonical name and follows the established term of art, not the KB language (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" and "Language")
|
||||||
|
- Open with a clear definition: what the concept is
|
||||||
|
- Give examples wherever they carry the understanding
|
||||||
|
- Link to the entities that implement or use the concept
|
||||||
|
- Under `provenance: sourced` or `mixed`, back hard facts inline with a `[^cite-id]` footnote -
|
||||||
|
`tools/wikitool cite add --page "<Title>" --source "Source - X"` mints the id and its definition
|
||||||
|
- Wikipedia register: factual, neutral, no marketing language
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Relevant Notes:
|
||||||
|
|
||||||
|
- Concept pages carry the wiki's theoretical underpinning
|
||||||
|
- Each concept type stands for a different category of abstract knowledge
|
||||||
|
- Concepts should be linked to from the entities that implement or use them
|
||||||
+5
-44
@@ -6,6 +6,7 @@ schema: types/concept.schema.yaml
|
|||||||
subtype_field: concept_type
|
subtype_field: concept_type
|
||||||
base_dir: concepts
|
base_dir: concepts
|
||||||
page_ref_fields: [related, sources]
|
page_ref_fields: [related, sources]
|
||||||
|
guidance: types/concept.guidance.md
|
||||||
layout:
|
layout:
|
||||||
architecture: {dir: architectures, title: Architekturen}
|
architecture: {dir: architectures, title: Architekturen}
|
||||||
pattern: {dir: patterns, title: Muster}
|
pattern: {dir: patterns, title: Muster}
|
||||||
@@ -17,32 +18,10 @@ layout:
|
|||||||
|
|
||||||
# Concept
|
# Concept
|
||||||
|
|
||||||
`concept` is the type for abstract ideas, methodologies, patterns and architectural decisions. Concepts provide the theoretical and methodological ground that entities implement or follow.
|
This instance's configuration for the `concept` type: its frontmatter fields as this schema
|
||||||
|
requires them, and the page skeleton `wikitool new concept` scaffolds. When to use this type and
|
||||||
<!-- wikitool:toc -->
|
how to write a conforming page is [types/concept.guidance.md](concept.guidance.md) -
|
||||||
## Contents
|
`tools/wikitool types describe concept` composes both into one answer.
|
||||||
|
|
||||||
- [When to use](#when-to-use)
|
|
||||||
- [When NOT to use](#when-not-to-use)
|
|
||||||
- [Frontmatter](#frontmatter)
|
|
||||||
- [Authoring guidance](#authoring-guidance)
|
|
||||||
- [Template](#template)
|
|
||||||
<!-- /wikitool:toc -->
|
|
||||||
|
|
||||||
## When to use
|
|
||||||
|
|
||||||
- Documenting an architectural pattern or design approach
|
|
||||||
- Explaining a design pattern or a programming methodology
|
|
||||||
- Describing a communication protocol or standard
|
|
||||||
- Recording a workflow or process that recurs across projects
|
|
||||||
- Documenting an architectural decision (ADR) or design decision
|
|
||||||
- Analysing a recurring problem and its solutions
|
|
||||||
|
|
||||||
## When NOT to use
|
|
||||||
|
|
||||||
- For concrete things such as projects, systems or tools (use `entity`)
|
|
||||||
- For source documents or ingested material (use `source`)
|
|
||||||
- For comparative analyses of concrete subjects (use `comparison`)
|
|
||||||
|
|
||||||
## Frontmatter
|
## Frontmatter
|
||||||
|
|
||||||
@@ -58,16 +37,6 @@ layout:
|
|||||||
| `provenance` | Yes | sourced, general or mixed |
|
| `provenance` | Yes | sourced, general or mixed |
|
||||||
| `summary` | Yes | One-liner for `kb/index.md` |
|
| `summary` | Yes | One-liner for `kb/index.md` |
|
||||||
|
|
||||||
## Authoring guidance
|
|
||||||
|
|
||||||
- The title is the concept's canonical name and follows the established term of art, not the KB language (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" and "Language")
|
|
||||||
- Open with a clear definition: what the concept is
|
|
||||||
- Give examples wherever they carry the understanding
|
|
||||||
- Link to the entities that implement or use the concept
|
|
||||||
- Under `provenance: sourced` or `mixed`, back hard facts inline with a `[^cite-id]` footnote -
|
|
||||||
`tools/wikitool cite add --page "<Title>" --source "Source - X"` mints the id and its definition
|
|
||||||
- Wikipedia register: factual, neutral, no marketing language
|
|
||||||
|
|
||||||
## Template
|
## Template
|
||||||
|
|
||||||
The block below is page material, so it is written in this instance's KB language
|
The block below is page material, so it is written in this instance's KB language
|
||||||
@@ -112,11 +81,3 @@ The value behind `**Typ:**` stays the English enum value - that is what `search
|
|||||||
on. When `wikitool xref` adds a relationship, the two tool-managed sections for relationships and
|
on. When `wikitool xref` adds a relationship, the two tool-managed sections for relationships and
|
||||||
cross-references appear as well; what they are called is the instance's decision in
|
cross-references appear as well; what they are called is the instance's decision in
|
||||||
`kb/CONVENTIONS.md` (`sections:`).
|
`kb/CONVENTIONS.md` (`sections:`).
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Additional notes:
|
|
||||||
|
|
||||||
- Concept pages carry the wiki's theoretical underpinning
|
|
||||||
- Each concept type stands for a different category of abstract knowledge
|
|
||||||
- Concepts should be linked to from the entities that implement or use them
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
type: types/type-guidance.md
|
||||||
|
name: entity
|
||||||
|
description: When to write an entity page instead of a neighboring type, and how to write a conforming one - citation, linking and register rules that hold regardless of which entity_type values this instance chose
|
||||||
|
---
|
||||||
|
|
||||||
|
# Entity Guidance
|
||||||
|
|
||||||
|
`entity` is the type for concrete things: projects, systems, tools, technologies or people.
|
||||||
|
Entities are the primary building blocks of the knowledge graph.
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
|
||||||
|
- Representing a software project, an initiative or a piece of work
|
||||||
|
- Documenting a running system, service or infrastructure component
|
||||||
|
- Describing a CLI tool, utility or software library
|
||||||
|
- Recording information about a language, a framework or a protocol
|
||||||
|
- Documenting a person, a team or an organization
|
||||||
|
|
||||||
|
## When NOT to use
|
||||||
|
|
||||||
|
- For abstract concepts, patterns or methodologies (use `concept`)
|
||||||
|
- For source documents or ingested material (use `source`)
|
||||||
|
- For comparative analyses (use `comparison`)
|
||||||
|
|
||||||
|
## Authoring guidance
|
||||||
|
|
||||||
|
- The title is the entity's canonical name and follows the subject's established designation, not the KB language (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" and "Language")
|
||||||
|
- The main description goes near the top
|
||||||
|
- Link to related entities and concepts wherever relationships exist
|
||||||
|
- Under `provenance: sourced` or `mixed`, back hard facts inline with a `[^cite-id]` footnote -
|
||||||
|
`tools/wikitool cite add --page "<Title>" --source "Source - X"` mints the id and its definition
|
||||||
|
- Wikipedia register: factual, neutral, no marketing language
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Relevant Notes:
|
||||||
|
|
||||||
|
- Entity pages form the concrete inventory of the knowledge base
|
||||||
|
- Every entity type (project, system, tool and so on) is a variant of this base type
|
||||||
|
- The relationships between entities carry the knowledge graph's interconnection
|
||||||
+5
-42
@@ -6,6 +6,7 @@ schema: types/entity.schema.yaml
|
|||||||
subtype_field: entity_type
|
subtype_field: entity_type
|
||||||
base_dir: entities
|
base_dir: entities
|
||||||
page_ref_fields: [related, sources]
|
page_ref_fields: [related, sources]
|
||||||
|
guidance: types/entity.guidance.md
|
||||||
layout:
|
layout:
|
||||||
project: {dir: projects, title: Projekte}
|
project: {dir: projects, title: Projekte}
|
||||||
system: {dir: systems, title: Systeme}
|
system: {dir: systems, title: Systeme}
|
||||||
@@ -16,31 +17,10 @@ layout:
|
|||||||
|
|
||||||
# Entity
|
# Entity
|
||||||
|
|
||||||
`entity` is the type for concrete things: projects, systems, tools, technologies or people. Entities are the primary building blocks of the knowledge graph.
|
This instance's configuration for the `entity` type: its frontmatter fields as this schema
|
||||||
|
requires them, and the page skeleton `wikitool new entity` scaffolds. When to use this type and
|
||||||
<!-- wikitool:toc -->
|
how to write a conforming page is [types/entity.guidance.md](entity.guidance.md) -
|
||||||
## Contents
|
`tools/wikitool types describe entity` composes both into one answer.
|
||||||
|
|
||||||
- [When to use](#when-to-use)
|
|
||||||
- [When NOT to use](#when-not-to-use)
|
|
||||||
- [Frontmatter](#frontmatter)
|
|
||||||
- [Authoring guidance](#authoring-guidance)
|
|
||||||
- [Template](#template)
|
|
||||||
<!-- /wikitool:toc -->
|
|
||||||
|
|
||||||
## When to use
|
|
||||||
|
|
||||||
- Representing a software project, an initiative or a piece of work
|
|
||||||
- Documenting a running system, service or infrastructure component
|
|
||||||
- Describing a CLI tool, utility or software library
|
|
||||||
- Recording information about a language, a framework or a protocol
|
|
||||||
- Documenting a person, a team or an organization
|
|
||||||
|
|
||||||
## When NOT to use
|
|
||||||
|
|
||||||
- For abstract concepts, patterns or methodologies (use `concept`)
|
|
||||||
- For source documents or ingested material (use `source`)
|
|
||||||
- For comparative analyses (use `comparison`)
|
|
||||||
|
|
||||||
## Frontmatter
|
## Frontmatter
|
||||||
|
|
||||||
@@ -56,15 +36,6 @@ layout:
|
|||||||
| `provenance` | Yes | sourced, general or mixed |
|
| `provenance` | Yes | sourced, general or mixed |
|
||||||
| `summary` | Yes | One-liner for `kb/index.md` |
|
| `summary` | Yes | One-liner for `kb/index.md` |
|
||||||
|
|
||||||
## Authoring guidance
|
|
||||||
|
|
||||||
- The title is the entity's canonical name and follows the subject's established designation, not the KB language (`kb/CONTRACT.md` § "Titles are identifiers", `kb/CONVENTIONS.md` §§ "Naming" and "Language")
|
|
||||||
- The main description goes near the top
|
|
||||||
- Link to related entities and concepts wherever relationships exist
|
|
||||||
- Under `provenance: sourced` or `mixed`, back hard facts inline with a `[^cite-id]` footnote -
|
|
||||||
`tools/wikitool cite add --page "<Title>" --source "Source - X"` mints the id and its definition
|
|
||||||
- Wikipedia register: factual, neutral, no marketing language
|
|
||||||
|
|
||||||
## Template
|
## Template
|
||||||
|
|
||||||
The block below is page material, so it is written in this instance's KB language
|
The block below is page material, so it is written in this instance's KB language
|
||||||
@@ -102,11 +73,3 @@ The relationships section is deliberately **not** in the template: it is a gener
|
|||||||
`wikitool xref` creates between markers on the first edge entry and re-renders from `related:`.
|
`wikitool xref` creates between markers on the first edge entry and re-renders from `related:`.
|
||||||
An author never writes into it. The value behind `**Typ:**` stays the English enum value - that is
|
An author never writes into it. The value behind `**Typ:**` stays the English enum value - that is
|
||||||
what `search --field` filters on.
|
what `search --field` filters on.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Additional notes:
|
|
||||||
|
|
||||||
- Entity pages form the concrete inventory of the knowledge base
|
|
||||||
- Every entity type (project, system, tool and so on) is a variant of this base type
|
|
||||||
- The relationships between entities carry the knowledge graph's interconnection
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
type: types/type-guidance.md
|
||||||
|
name: source
|
||||||
|
description: When to write a source page instead of a neighboring type, and how to write a conforming one - capture-field, provenance and not-extracted rules that hold regardless of which source_type values this instance chose
|
||||||
|
---
|
||||||
|
|
||||||
|
# Source Guidance
|
||||||
|
|
||||||
|
`source` is the type for pages that summarize and catalogue ingested raw material. Source pages
|
||||||
|
are the bridge between the `raw/` layer (immutable source files) and the `kb/` layer (compiled
|
||||||
|
knowledge). One source page stands for **one logical source**, which may span several raw files.
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
|
||||||
|
- Summarizing a single external article, document or specification
|
||||||
|
- Recording several related notes or meeting records as one source
|
||||||
|
- Documenting an ingested PDF, manual or other document
|
||||||
|
- Capturing information about an image or a diagram
|
||||||
|
|
||||||
|
## When NOT to use
|
||||||
|
|
||||||
|
- For compiled knowledge (use `entity` or `concept`)
|
||||||
|
- For comparative analyses (use `comparison`)
|
||||||
|
- For original wiki content not derived from raw material
|
||||||
|
|
||||||
|
## Authoring guidance
|
||||||
|
|
||||||
|
- `source_type` has **no default** - `wikitool new source` refuses without an explicit value. Where the category is unclear, set `unclassified` rather than guessing; that is a visible catalog slot with an advisory `lint` finding, not a dumping ground. What separates `analysis` from `document`, and which area holds which value, is a call this instance's own collection contract makes - see `kb/sources/COLLECTION.md`
|
||||||
|
- `raw_files` lists every raw file this source covers (one source page per logical source, not per file)
|
||||||
|
- `fidelity` and `authority` are **capture fields** (`capture_fields:` on this type): recorded at the drop point and not freely changeable afterwards. `wikitool raw accept --fidelity <value> --authority <value>` refuses without both; without `--page` it instead prints the ready-made `new source --set fidelity=... --set authority=...` line, which `new source` in turn refuses without both values. `wikitool touch --set fidelity=<value>` only writes while the field is absent - where a value already stands, it refuses and points at `raw accept --replaces` as the one correction path (a corrected capture is a new edition, not an edit). `unknown` is backfill-only: only `wikitool touch` may write it, never `raw accept` or `new source` - the same construction as `source_language` uses for pages that predate this rule. What the values mean and how they differ: [raw/CONTRACT.md](../raw/CONTRACT.md#getting-a-file-in-incoming)
|
||||||
|
- For external articles, always set `source_url` to the origin URL
|
||||||
|
- Set `source_language` to the raw material's language, not the page's
|
||||||
|
- The page is written in the KB language, whatever language the source is in; verbatim passages are quoted in the original (`kb/CONVENTIONS.md` § "Language")
|
||||||
|
- Summarize the key claims in the summary section
|
||||||
|
- Put anything actionable in the action items section
|
||||||
|
- Put deliberate omissions in the not-extracted section - see below
|
||||||
|
- Link the entities and concepts mentioned under related entities/concepts
|
||||||
|
|
||||||
|
## Not Extracted
|
||||||
|
|
||||||
|
The decision that material should *not* be taken over cannot be reconstructed: nothing in the
|
||||||
|
repository can re-derive it, and `sources coverage` only knows whether a raw file is claimed by
|
||||||
|
some source page - never whether anyone decided about its contents. Left unwritten, the same
|
||||||
|
source is renegotiated on every later pass.
|
||||||
|
|
||||||
|
- Record every deliberate omission with a **reason**, not just a filename.
|
||||||
|
- Mandatory where the ingest ran through `instructions/ingest-large-tree.md` - on both axes: for
|
||||||
|
a tree ingest the section records what was not taken from the tree; for a thematically broad
|
||||||
|
single source, which named subjects got no page of their own, and why. Optional for a single
|
||||||
|
small file - but an empty section still beats a missing one.
|
||||||
|
- Belongs on the source page, not in `kb/log.md`: it is a statement about *this* source, and the
|
||||||
|
log is chronological rather than per-source.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Relevant Notes:
|
||||||
|
|
||||||
|
- Source pages are the authoritative catalogue of what raw material has been ingested
|
||||||
|
- **One raw file, one owner.** A raw file appears in exactly one `raw_files:` - that page is responsible for keeping it summarized. Any number of pages may **cite** it via `[^cite-id]`; a citation is reuse, `raw_files:` is a maintenance responsibility. With two claimants it is undefined which page has to be brought up to date when the raw file changes - and then both rot quietly
|
||||||
|
- Source pages make knowledge traceable back to the original raw material
|
||||||
|
- `raw_files:` holds concrete existing file paths, never directories
|
||||||
|
- A `raw_files:` list beyond roughly 15 entries indicates the cut was too coarse - the source should have been split into several source pages via `instructions/ingest-large-tree.md`
|
||||||
|
- `entities:` plus `concepts:` beyond roughly 20 entries is the counterpart on the other axis: not cut too little, but compiled too much at once. Such a source is not split - a raw file has one owner - it needed the extract pass from `instructions/ingest-large-tree.md` § "A broad source is not cut", so that not every named subject gets a page
|
||||||
+5
-61
@@ -8,6 +8,7 @@ base_dir: sources
|
|||||||
title_prefix: "Source - "
|
title_prefix: "Source - "
|
||||||
page_ref_fields: [entities, concepts]
|
page_ref_fields: [entities, concepts]
|
||||||
capture_fields: [fidelity, authority]
|
capture_fields: [fidelity, authority]
|
||||||
|
guidance: types/source.guidance.md
|
||||||
layout:
|
layout:
|
||||||
transcript: {dir: transcripts, title: Transkripte}
|
transcript: {dir: transcripts, title: Transkripte}
|
||||||
analysis: {dir: analyses, title: Analysen}
|
analysis: {dir: analyses, title: Analysen}
|
||||||
@@ -20,31 +21,10 @@ layout:
|
|||||||
|
|
||||||
# Source
|
# Source
|
||||||
|
|
||||||
`source` is the type for pages that summarize and catalogue ingested raw material. Source pages are the bridge between the `raw/` layer (immutable source files) and the `kb/` layer (compiled knowledge). One source page stands for **one logical source**, which may span several raw files.
|
This instance's configuration for the `source` type: its frontmatter fields as this schema
|
||||||
|
requires them, and the page skeleton `wikitool new source` scaffolds. When to use this type and
|
||||||
<!-- wikitool:toc -->
|
how to write a conforming page is [types/source.guidance.md](source.guidance.md) -
|
||||||
## Contents
|
`tools/wikitool types describe source` composes both into one answer.
|
||||||
|
|
||||||
- [When to use](#when-to-use)
|
|
||||||
- [When NOT to use](#when-not-to-use)
|
|
||||||
- [Frontmatter](#frontmatter)
|
|
||||||
- [Authoring guidance](#authoring-guidance)
|
|
||||||
- [Not Extracted](#not-extracted)
|
|
||||||
- [Template](#template)
|
|
||||||
<!-- /wikitool:toc -->
|
|
||||||
|
|
||||||
## When to use
|
|
||||||
|
|
||||||
- Summarizing a single external article, document or specification
|
|
||||||
- Recording several related notes or meeting records as one source
|
|
||||||
- Documenting an ingested PDF, manual or other document
|
|
||||||
- Capturing information about an image or a diagram
|
|
||||||
|
|
||||||
## When NOT to use
|
|
||||||
|
|
||||||
- For compiled knowledge (use `entity` or `concept`)
|
|
||||||
- For comparative analyses (use `comparison`)
|
|
||||||
- For original wiki content not derived from raw material
|
|
||||||
|
|
||||||
## Frontmatter
|
## Frontmatter
|
||||||
|
|
||||||
@@ -67,31 +47,6 @@ layout:
|
|||||||
## Authoring guidance
|
## Authoring guidance
|
||||||
|
|
||||||
- The title starts with "Source - ", followed by the name of the source
|
- The title starts with "Source - ", followed by the name of the source
|
||||||
- `source_type` has **no default** - `wikitool new source` refuses without an explicit value. Where the category is unclear, set `unclassified` rather than guessing; that is a visible catalog slot with an advisory `lint` finding, not a dumping ground. What separates `analysis` from `document`, and which area holds which value: [kb/sources/COLLECTION.md](../kb/sources/COLLECTION.md)
|
|
||||||
- `raw_files` lists every raw file this source covers (one source page per logical source, not per file)
|
|
||||||
- `fidelity` and `authority` are **capture fields** (`capture_fields:` above): recorded at the drop point and not freely changeable afterwards. `wikitool raw accept --fidelity <value> --authority <value>` refuses without both; without `--page` it instead prints the ready-made `new source --set fidelity=... --set authority=...` line, which `new source` in turn refuses without both values. `wikitool touch --set fidelity=<value>` only writes while the field is absent - where a value already stands, it refuses and points at `raw accept --replaces` as the one correction path (a corrected capture is a new edition, not an edit). `unknown` is backfill-only: only `wikitool touch` may write it, never `raw accept` or `new source` - the same construction as `source_language` uses for pages that predate this rule. What the values mean and how they differ: [raw/CONTRACT.md](../raw/CONTRACT.md#getting-a-file-in-incoming)
|
|
||||||
- For external articles, always set `source_url` to the origin URL
|
|
||||||
- Set `source_language` to the raw material's language, not the page's
|
|
||||||
- The page is written in the KB language, whatever language the source is in; verbatim passages are quoted in the original (`kb/CONVENTIONS.md` § "Language")
|
|
||||||
- Summarize the key claims in the summary section
|
|
||||||
- Put anything actionable in the action items section
|
|
||||||
- Put deliberate omissions in the not-extracted section - see below
|
|
||||||
- Link the entities and concepts mentioned under related entities/concepts
|
|
||||||
|
|
||||||
## Not Extracted
|
|
||||||
|
|
||||||
The decision that material should *not* be taken over cannot be reconstructed: nothing in the
|
|
||||||
repository can re-derive it, and `sources coverage` only knows whether a raw file is claimed by
|
|
||||||
some source page - never whether anyone decided about its contents. Left unwritten, the same
|
|
||||||
source is renegotiated on every later pass.
|
|
||||||
|
|
||||||
- Record every deliberate omission with a **reason**, not just a filename.
|
|
||||||
- Mandatory where the ingest ran through `instructions/ingest-large-tree.md` - on both axes: for
|
|
||||||
a tree ingest the section records what was not taken from the tree; for a thematically broad
|
|
||||||
single source, which named subjects got no page of their own, and why. Optional for a single
|
|
||||||
small file - but an empty section still beats a missing one.
|
|
||||||
- Belongs on the source page, not in `kb/log.md`: it is a statement about *this* source, and the
|
|
||||||
log is chronological rather than per-source.
|
|
||||||
|
|
||||||
## Template
|
## Template
|
||||||
|
|
||||||
@@ -139,14 +94,3 @@ TODO: 2-3 Absätze zu den Kernaussagen des Quellmaterials.
|
|||||||
linked and cited under. The value behind `**Typ:**` stays the English enum value. When
|
linked and cited under. The value behind `**Typ:**` stays the English enum value. When
|
||||||
`wikitool cite` adds a citation, the tool-managed footnote block appears at the end of the page;
|
`wikitool cite` adds a citation, the tool-managed footnote block appears at the end of the page;
|
||||||
what it is called is the instance's decision in `kb/CONVENTIONS.md` (`sections:`).
|
what it is called is the instance's decision in `kb/CONVENTIONS.md` (`sections:`).
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Additional notes:
|
|
||||||
|
|
||||||
- Source pages are the authoritative catalogue of what raw material has been ingested
|
|
||||||
- **One raw file, one owner.** A raw file appears in exactly one `raw_files:` - that page is responsible for keeping it summarized. Any number of pages may **cite** it via `[^cite-id]`; a citation is reuse, `raw_files:` is a maintenance responsibility. With two claimants it is undefined which page has to be brought up to date when the raw file changes - and then both rot quietly
|
|
||||||
- Source pages make knowledge traceable back to the original raw material
|
|
||||||
- `raw_files:` holds concrete existing file paths, never directories
|
|
||||||
- A `raw_files:` list beyond roughly 15 entries indicates the cut was too coarse - the source should have been split into several source pages via `instructions/ingest-large-tree.md`
|
|
||||||
- `entities:` plus `concepts:` beyond roughly 20 entries is the counterpart on the other axis: not cut too little, but compiled too much at once. Such a source is not split - a raw file has one owner - it needed the extract pass from `instructions/ingest-large-tree.md` § "A broad source is not cut", so that not every named subject gets a page
|
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
type: types/type-spec.md
|
||||||
|
name: type-guidance
|
||||||
|
description: "Stack-owned authoring prose for a root:kb page type - the machine-independent half a type-spec's optional guidance: field links to"
|
||||||
|
schema: types/type-guidance.schema.yaml
|
||||||
|
---
|
||||||
|
|
||||||
|
# Type Guidance
|
||||||
|
|
||||||
|
`type-guidance` describes the shape of one `types/<name>.guidance.md` file: the stack-owned half
|
||||||
|
of a `root: kb` page type's contract. A page type-spec ([types/type-spec.md](type-spec.md) §
|
||||||
|
"Who owns a type-spec") splits at the audience line - what an agent needs to know to write a
|
||||||
|
conforming page, versus what this instance's schema and template concretely are - and a guidance
|
||||||
|
file is the machine-independent half of that split, linked from the type-spec it documents via
|
||||||
|
that file's own `guidance:` frontmatter field.
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
- Writing or improving the generic authoring rules for a `root: kb` type: when to choose it, when
|
||||||
|
not to, and mechanism-level advice that holds regardless of which enum values, directory names
|
||||||
|
or template text this instance chose - citation mechanics, provenance rules, what a conforming
|
||||||
|
page does or does not claim
|
||||||
|
- Anything a later stack release would want every adopting instance to receive automatically,
|
||||||
|
the way a fix to `tools/` already does
|
||||||
|
|
||||||
|
## When NOT to Use
|
||||||
|
|
||||||
|
- Enum values, `layout:` titles, directory names or template text. Those name *this* instance's
|
||||||
|
choices and stay in the type-spec itself - the file `guidance:` points *from*, never the one it
|
||||||
|
points *to*
|
||||||
|
- A type an instance writes entirely for itself. `guidance:` is optional; a type-spec that omits
|
||||||
|
it is described from its own body alone, exactly as every type-spec worked before this type
|
||||||
|
existed
|
||||||
|
|
||||||
|
## Frontmatter
|
||||||
|
|
||||||
|
| Field | Required | Use |
|
||||||
|
|---|---:|---|
|
||||||
|
| `type` | Yes | `types/type-guidance.md` |
|
||||||
|
| `name` | Yes | The page type this documents, e.g. `entity` - matches that type-spec's own `name:` |
|
||||||
|
| `description` | Yes | Retrieval description for this guidance document |
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- Linked from the type-spec it documents via that file's `guidance:` frontmatter field - a
|
||||||
|
repo-relative path, resolved the same way `schema:` is
|
||||||
|
- `tools/wikitool types describe <name>` composes both halves into one answer; an agent asking
|
||||||
|
for a type's contract never needs to know it comes from two files
|
||||||
|
- Written in the control plane's English ([AGENTS.md](../AGENTS.md) § File naming), like a
|
||||||
|
type-spec's own authoring prose - only the type-spec's `## Template` block and its `layout:`
|
||||||
|
titles are page material
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Relevant Notes:
|
||||||
|
|
||||||
|
- Ships verbatim with every distribution; the type-spec it documents ships only as a `.template`
|
||||||
|
where `root: kb` - see `docs/ownership-and-templates.md` § "Where the file boundary used to
|
||||||
|
strain"
|
||||||
|
- No `## Template` section here: a guidance file's body is prose an author reads, not a body
|
||||||
|
structure `wikitool new` would scaffold. `type-guidance` declares no `base_dir:` for the same
|
||||||
|
reason `lint-report` does, and `wikitool new type-guidance` refuses for the same reason
|
||||||
|
- An instance that adopted its type-specs before this type existed sees nothing change until it
|
||||||
|
takes the offered migration that adds `guidance:` to each one - see
|
||||||
|
`instructions/migrations/` for the document
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# YAML Schema for type-guidance documents
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
const: "types/type-guidance.md"
|
||||||
|
description: Must reference the type-guidance type-spec
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: >-
|
||||||
|
The page type this guidance documents, e.g. 'entity' - matches that
|
||||||
|
type-spec's own `name:` field
|
||||||
|
minLength: 1
|
||||||
|
pattern: "^[a-z][a-z0-9-]*$"
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
description: Retrieval description for this guidance document
|
||||||
|
minLength: 10
|
||||||
|
required:
|
||||||
|
- type
|
||||||
|
- name
|
||||||
|
- description
|
||||||
|
additionalProperties: false
|
||||||
+37
-18
@@ -33,6 +33,7 @@ A type-spec doc is the authoring and validation contract for one Chemenu artifac
|
|||||||
- `description`: retrieval description for the type contract.
|
- `description`: retrieval description for the type contract.
|
||||||
- `schema`: repo-relative path to the `.schema.yaml` file that validates artifacts of this type, or `null` when the type has no schema.
|
- `schema`: repo-relative path to the `.schema.yaml` file that validates artifacts of this type, or `null` when the type has no schema.
|
||||||
- `subtype_field` (optional): the frontmatter field name on instances of this type that carries a subtype/category (e.g. `entity_type`), or omitted if this type has no such field.
|
- `subtype_field` (optional): the frontmatter field name on instances of this type that carries a subtype/category (e.g. `entity_type`), or omitted if this type has no such field.
|
||||||
|
- `guidance` (optional): repo-relative path to a `types/type-guidance.md`-shaped document holding this type's stack-owned authoring prose - see § "Anatomy of a type" below. Meaningful only on a `root: kb` type-spec; omit for a type an instance writes entirely for itself.
|
||||||
|
|
||||||
## Validation Contract
|
## Validation Contract
|
||||||
|
|
||||||
@@ -65,28 +66,41 @@ frontmatter before anyone drew it:
|
|||||||
| Type-spec | Describes | Owned by | Ships as |
|
| Type-spec | Describes | Owned by | Ships as |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `root: kb` (`entity`, `concept`, `source`, `comparison`) | A page **this instance** writes | The instance | `types/<name>.md.template` plus its `.schema.yaml.template`, adopted by a rename |
|
| `root: kb` (`entity`, `concept`, `source`, `comparison`) | A page **this instance** writes | The instance | `types/<name>.md.template` plus its `.schema.yaml.template`, adopted by a rename |
|
||||||
| `root: repo` (`instruction`), no `base_dir` (`lint-report`), and `type-spec` itself | A stack artifact | The stack | Verbatim |
|
| `root: repo` (`instruction`), no `base_dir` (`lint-report`), and `type-spec`/`type-guidance` themselves | A stack artifact | The stack | Verbatim |
|
||||||
|
|
||||||
A page type-spec's prose and its `## Template` body are therefore the instance's to rewrite, and
|
A page type-spec's frontmatter configuration and its `## Template` body are therefore the
|
||||||
an upgrade does not take that back. Improvements to a shipped default reach it as an *offered*
|
instance's to rewrite, and an upgrade does not take that back. Its generic authoring prose is the
|
||||||
migration ([instructions/CONTRACT.md](../instructions/CONTRACT.md#instructionsmigrations)),
|
opposite: where the type-spec declares `guidance:`, that prose lives in a separate, stack-owned
|
||||||
never by overwriting.
|
`types/<name>.guidance.md` (§ "Anatomy of a type" below) that ships verbatim and is overwritten
|
||||||
|
by an upgrade like any other machinery file - the type-spec it documents does not have to be
|
||||||
|
re-adopted, or even touched, for that improvement to arrive. A type-spec that declares no
|
||||||
|
`guidance:` (a type an instance writes entirely for itself) has no stack half to receive; it is
|
||||||
|
described from its own body alone, exactly as every type-spec worked before this split existed.
|
||||||
|
An *existing* instance that adopted its type-specs before `guidance:` existed takes this as an
|
||||||
|
*offered* migration ([instructions/CONTRACT.md](../instructions/CONTRACT.md#instructionsmigrations))
|
||||||
|
rather than something an upgrade applies on its own - see `instructions/migrations/` for the
|
||||||
|
document.
|
||||||
|
|
||||||
**Ownership and language are not the same question here**, and this is the one file in the tree
|
**Ownership and language still do not line up with each other**, and that stays true even though
|
||||||
where they come apart. Owning the file means the instance may rewrite any of it; the language it
|
the authoring prose now has its own file rather than sharing one with the template. Owning the
|
||||||
writes in still follows what the line is *for*, per
|
type-spec means the instance may rewrite its frontmatter and its `## Template` body; a guidance
|
||||||
[AGENTS.md § File naming](../AGENTS.md#file-naming):
|
file it links is never the instance's to rewrite at all - it is the stack's, the same way
|
||||||
|
`tools/CONTRACT.md` is. What each part is *written in* still follows what the line is *for*, per
|
||||||
|
[AGENTS.md § File naming](../AGENTS.md#file-naming), independent of which file it lives in or who
|
||||||
|
owns that file:
|
||||||
|
|
||||||
| Part of a page type-spec | Audience | Language |
|
| Part of a `root: kb` type's contract | Lives in | Audience | Language |
|
||||||
|---|---|---|
|
|---|---|---|---|
|
||||||
| Authoring guidance, frontmatter table, when to use / not to use | An agent writing a page | The control plane's — English |
|
| When to use / not to use, authoring guidance | The `guidance:` file, where declared | An agent writing a page | The control plane's — English |
|
||||||
| The `## Template` body, and the `layout:` titles that head a catalog section | The page itself | The instance's KB language (`kb/CONVENTIONS.md` `language:`) |
|
| The frontmatter table documenting this instance's own fields | The type-spec itself | An agent writing a page | The control plane's — English |
|
||||||
| Field names, enum values, `dir:` values, `type:` paths | The machine | Neither — identifiers, never translated |
|
| The `## Template` body, and the `layout:` titles that head a catalog section | The type-spec itself | The page itself | The instance's KB language (`kb/CONVENTIONS.md` `language:`) |
|
||||||
|
| Field names, enum values, `dir:` values, `type:` paths | Either file's frontmatter | The machine | Neither — identifiers, never translated |
|
||||||
|
|
||||||
That is the same prose/identifier cut [kb/CONTRACT.md](../kb/CONTRACT.md#language-and-identifiers)
|
That is the same prose/identifier cut [kb/CONTRACT.md](../kb/CONTRACT.md#language-and-identifiers)
|
||||||
makes *inside* a page, applied one level up. It binds a type-spec an instance writes for itself
|
makes *inside* a page, applied one level up. It binds a type-spec an instance writes for itself
|
||||||
exactly as it binds the four shipped ones: a new page type is instance-owned end to end, which
|
exactly as it binds the four shipped ones: a new page type is instance-owned end to end, which
|
||||||
settles who may change it, not which language each half is written in.
|
settles who may change it, not which language each half is written in - and it may declare its
|
||||||
|
own `guidance:` file if it wants the same shape, though nothing requires it to.
|
||||||
|
|
||||||
**What the stack still requires of the type layer is one line.** There must be a type-spec
|
**What the stack still requires of the type layer is one line.** There must be a type-spec
|
||||||
declaring `name: source` whose schema requires `raw_files:` — the whole `raw/` → `kb/`
|
declaring `name: source` whose schema requires `raw_files:` — the whole `raw/` → `kb/`
|
||||||
@@ -102,14 +116,19 @@ Python. Adding a type must require no code change.
|
|||||||
|
|
||||||
### Anatomy of a type
|
### Anatomy of a type
|
||||||
|
|
||||||
Each type is two files:
|
Each type is at least two files, and a `root: kb` type may be three:
|
||||||
|
|
||||||
| File | Owns |
|
| File | Owns |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `types/<name>.md` | The authoring contract: when to use the type, when not to, guidance, and the `## Template` block used to scaffold new pages |
|
| `types/<name>.md` | This instance's configuration: its frontmatter fields as this schema requires them, and the `## Template` block used to scaffold new pages. For a type with no `guidance:` (below), its own prose also carries the authoring contract - when to use the type, when not to |
|
||||||
| `types/<name>.schema.yaml` | The machine-checkable half: fields, types, enums, defaults, required-ness, `additionalProperties: false` |
|
| `types/<name>.schema.yaml` | The machine-checkable half: fields, types, enums, defaults, required-ness, `additionalProperties: false` |
|
||||||
|
| `types/<name>.guidance.md` (optional, `root: kb` only) | The stack-owned authoring contract: when to use the type, when not to, and mechanism-level advice that holds regardless of this instance's own enum values or template text - linked from the type-spec's own `guidance:` field. `types/type-guidance.md` is its contract |
|
||||||
|
|
||||||
This file is the self-referential root contract that both are validated against.
|
This file is the self-referential root contract every type-spec is validated against, and
|
||||||
|
`type-guidance.md` is validated against it the same way `lint-report.md` is - itself a
|
||||||
|
non-instantiable, contract-only type. `tools/wikitool types describe <name>` composes all of a
|
||||||
|
type's files into one answer regardless of how many there are; an agent asking for a type's
|
||||||
|
contract never needs to know it came from more than one file.
|
||||||
|
|
||||||
### Placement frontmatter
|
### Placement frontmatter
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user