177c7e9ce8
Files changed: - .gitea/workflows/ci.yml - AGENTS.md - CHANGES.md - VERSION - instructions/CONTRACT.md - instructions/link-taxonomy.md - instructions/migrations/4.0.0-link-taxonomy.md - instructions/setup-instance.md - kb/CONTRACT.md - kb/CONVENTIONS.md - kb/CONVENTIONS.md.template - kb/comparisons/COLLECTION.md - kb/concepts/COLLECTION.md - kb/entities/COLLECTION.md - kb/sources/COLLECTION.md - tools/CONTRACT.md - tools/README.md - tools/chemenu/blocks.py - tools/chemenu/cli.py - tools/chemenu/commands/cite_cmd.py - tools/chemenu/commands/dist_cmd.py - tools/chemenu/commands/docs_verify.py - tools/chemenu/commands/doctor.py - tools/chemenu/commands/links_cmd.py - tools/chemenu/commands/migrate_cmd.py - tools/chemenu/commands/new_page.py - tools/chemenu/commands/page_ops.py - tools/chemenu/commands/run_budget.py - tools/chemenu/commands/xref.py - tools/chemenu/conventions.py - tools/chemenu/corpus_diff.py - tools/chemenu/frontmatter_io.py - tools/chemenu/kb_collections.py - tools/chemenu/kb_state.py - tools/chemenu/links.py - tools/chemenu/lint_core.py - tools/chemenu/provenance.py - tools/chemenu/sections.py - tools/chemenu/tests/conftest.py - tools/chemenu/tests/test_blocks.py - tools/chemenu/tests/test_cite_cmd.py - tools/chemenu/tests/test_conventions.py - tools/chemenu/tests/test_dist_cmd.py - tools/chemenu/tests/test_doctor.py - tools/chemenu/tests/test_migrate_cmd.py - tools/chemenu/tests/test_new_page.py - tools/chemenu/tests/test_pipeline_l0.py - tools/chemenu/tests/test_types_cmd.py - tools/chemenu/tests/test_xref.py - types/concept.schema.yaml - types/entity.md - types/entity.schema.yaml - types/instruction.schema.yaml - types/type-spec.md - work/link-taxonomy-migration/README.md - work/link-taxonomy-migration/plan.md
188 lines
12 KiB
Markdown
188 lines
12 KiB
Markdown
# instructions/ - Instruction Layer Contract
|
|
|
|
Agent-directed procedure. Everything an agent is *told to do* lives here, and nowhere else.
|
|
|
|
**Quality goal:** executability + precision - every step actionable, every decision point
|
|
explicit, ambiguity eliminated. A vague prescription spends bounded context on interpretation
|
|
instead of action.
|
|
|
|
`instructions/` is not a pipeline stage and not a collection. It is part of the control plane,
|
|
alongside [AGENTS.md](../AGENTS.md).
|
|
|
|
## Two forms, three reference tiers
|
|
|
|
| Form | File | Loaded by |
|
|
|------|------|-----------|
|
|
| **Instruction** | `instructions/<name>.md` | A link from a skill, a contract, AGENTS.md, or CLAUDE.md - or run explicitly on request |
|
|
| **Skill** | `instructions/<name>/SKILL.md` | The agent harness, automatically, once published |
|
|
|
|
The Instruction/Skill split is **structural, not editorial**: a subdirectory containing a
|
|
`SKILL.md` is published; a flat `.md` file never is. Nothing else decides it, and no
|
|
frontmatter flag controls it.
|
|
|
|
The split exists because publication is not free. Every published skill's description sits in
|
|
the agent's context for the whole session, whether or not it is used. A procedure that runs
|
|
once a quarter earns a link, not a permanent slot.
|
|
|
|
Within the flat `instructions/<name>.md` form, `tools/wikitool instructions verify`'s
|
|
reference rule (below) has two further tiers, told apart by frontmatter `manual: true`:
|
|
|
|
| Tier | `manual:` | Referenced from AGENTS.md/CLAUDE.md/a contract/a skill/... | Linked from AGENTS.md, CLAUDE.md, or a skill | When |
|
|
|------|-----------|------------------------------------------------------|-----------------------------------|------|
|
|
| **Linked** | absent (default) | Required - `verify` reports it as dead otherwise | Allowed | The normal case: every instruction most agents will run |
|
|
| **Manual** | `true` | Not required, and a CONTRACT.md/COLLECTION.md/other-instruction mention is fine | Forbidden - `verify` reports it if it IS linked there | Rare, deliberate, or still experimental - must never be picked up implicitly. Named directly by the user, or mentioned as documentation, never followed as an automatic step |
|
|
|
|
AGENTS.md and CLAUDE.md are both "automatically loaded" for this purpose, but for disjoint
|
|
harnesses: AGENTS.md is read natively by every harness except Claude Code, and CLAUDE.md exists
|
|
because Claude Code does not read AGENTS.md on its own (see AGENTS.md's file-naming table). A
|
|
Claude-Code-only instruction is therefore reached from CLAUDE.md, not AGENTS.md - a link from
|
|
AGENTS.md would load it into every other harness's session too, where it may not even apply.
|
|
|
|
CLAUDE.md can reach it two ways, and the choice is about *when the decision is made*:
|
|
|
|
| From CLAUDE.md | Effect | Use for |
|
|
|---|---|---|
|
|
| `@instructions/<name>.md` | The whole file is in context for every session on this harness | A decision made in passing - while spawning a subagent, while picking a review level - where nobody would stop to open a document |
|
|
| A markdown link | Only the link line is in context; the body is read on demand | A procedure looked up deliberately, when its trigger is recognisable from the link alone |
|
|
|
|
An import is the strongest load in this layer - stronger than a skill, which puts only its
|
|
`description` in context - so it is also the most expensive. It is charged to every session on
|
|
that harness whether or not the session ever makes the decision, which is the bar each further
|
|
import has to clear. `tools/wikitool instructions verify` counts either form as a reference: both
|
|
put the filename in CLAUDE.md.
|
|
|
|
**A mention in README.md or CHANGES.md is not a reference.** Both describe the stack to a human
|
|
- the file-naming table makes README.md "never by an agent as instruction" - so a mention there
|
|
documents an instruction without deploying it to anyone. `verify` scans neither when asking
|
|
whether an instruction is still reachable, which is exactly why the answer means something. The
|
|
`instructions/dev/` boundary check below asks the opposite question - what would *dangle* in a
|
|
distributed instance - and does scan README.md, because `dist export` ships it verbatim.
|
|
|
|
Three kinds of file use the Manual tier today: [german-terminology.md](german-terminology.md), a
|
|
vocabulary consulted on demand rather than a procedure; [kb-profiles.md](kb-profiles.md), the
|
|
catalogue of authoring profiles an instance may adopt into its own `kb/CONVENTIONS.md` and
|
|
`COLLECTION.md` files; and every migration document (below).
|
|
|
|
## `instructions/migrations/`
|
|
|
|
A content migration is a Manual instruction with three extra frontmatter fields
|
|
(`types/instruction.schema.yaml`): `migrates_to:`, the stack version whose content shape it
|
|
produces; `migration_kind:` (`mechanical` | `assisted`); and `obligation:`
|
|
(`required` | `offered`, default `required`). It lives at
|
|
`instructions/migrations/<version>-<slug>.md`.
|
|
|
|
`migration_kind:` and `obligation:` are **two axes, not one**. The first says how the work is
|
|
carried out, the second whether it has to happen at all:
|
|
|
|
| `obligation:` | Means | `migrate status` |
|
|
|---|---|---|
|
|
| `required` | The content must reach the new shape or it no longer fits the machinery | Counted as outstanding; `migrate done` advances `kb_version` through it, in chain order |
|
|
| `offered` | A file the instance owns still works as it is, and the stack proposes a better default | Listed separately, never blocks, no ordering rule. `migrate done` records it in the applied ledger and leaves `kb_version` where it is |
|
|
|
|
Keeping them apart is what stops `migrate status` crying wolf: an instance nagged about an
|
|
improvement it declined stops reading the nag that means its content no longer fits its
|
|
machinery. And because taking an offer deliberately does not move the version, the **applied
|
|
ledger** - not `kb_version` - is what makes an offer stop being offered; without that record
|
|
there is no way to tell a taken offer from an ignored one.
|
|
|
|
An `offered` migration is what makes an instance-owned file upgradeable at all. `dist export`
|
|
records a sha256 per shipped file in `.wikitool-release.json`, so `migrate status` can say which
|
|
of those files the instance edited and which it merely received - the first have to be
|
|
reconciled by a person, the second can simply be copied over.
|
|
|
|
The tier fits exactly: a migration must never be picked up implicitly - it rewrites the corpus -
|
|
and it is referenced by nothing, because `tools/wikitool migrate status` finds it by reading the
|
|
directory and comparing `migrates_to:` against this instance's `kb_version`. That is also why
|
|
these files are ordinary instructions rather than a new stage: `dist export` already ships
|
|
`instructions/`, so a migration reaches every distributed instance without a second export path.
|
|
|
|
Writing one is [migrate-corpus.md](migrate-corpus.md), which also holds the procedure for
|
|
carrying a migration out. The baseline is `1.0.0` - nothing older has a document.
|
|
|
|
## `instructions/dev/`
|
|
|
|
A fourth, orthogonal split: material relevant only to developing the tool stack - procedures for extending
|
|
`tools/wikitool`, the type schema, or this layer itself, rather than operating on wiki content -
|
|
lives under `instructions/dev/`, one level in. `tools/wikitool dist export` prunes that whole
|
|
directory, unconditionally and one-way: there is no command that adds it back to a distributed
|
|
instance. This is a whole-directory exclusion, distinct from the
|
|
`<!-- dist:strip-start/end -->` marker convention ([tools/CONTRACT.md](../tools/CONTRACT.md)),
|
|
which removes marked *content* from an otherwise-shipped file rather than excluding a file
|
|
outright.
|
|
|
|
This is orthogonal to the Linked/Manual split above, not a third value of the same field: a
|
|
`instructions/dev/*.md` file still carries `manual:` or not, exactly like any other instruction,
|
|
and still needs a reference from somewhere for `verify`'s ordinary orphan check. What
|
|
`instructions/dev/` adds on top is a hard boundary in the other direction - `tools/wikitool
|
|
instructions verify` also reports anything under it that is referenced from **outside** it,
|
|
because such a reference would dangle the moment `dist export` runs. A skill switching a session
|
|
into this mode is nested under `instructions/dev/` too, for the same reason: it must never reach
|
|
a distributed instance either.
|
|
|
|
The one sanctioned crossing is a routing line from AGENTS.md into `instructions/dev/`, and it
|
|
uses the marker convention to stay honest: wrapped in `<!-- dist:strip-start/end -->`, so `dist
|
|
export` removes the line and the directory it points at together, and `verify`'s boundary check
|
|
skips marker-block content before scanning, exempting exactly that line and nothing else.
|
|
|
|
## Publishing
|
|
|
|
`tools/wikitool instructions sync` **copies** each skill directory into `.agents/skills/` (read
|
|
natively by GitHub Copilot, Codex CLI and Mistral Vibe) and `.claude/skills/` (Claude Code reads
|
|
nothing else).
|
|
|
|
Both targets are generated and gitignored. A fresh clone therefore has no skills until
|
|
`sync` runs - see [bootstrap.md](bootstrap.md).
|
|
|
|
Copies, not symlinks: a symlink cannot go stale but is unreliable on Windows checkouts and
|
|
does not survive being archived or copied. The price of a copy is drift, and drift is what
|
|
`tools/wikitool instructions verify` checks - byte for byte against the source.
|
|
|
|
## Writing an instruction
|
|
|
|
Scaffold with `tools/wikitool new instruction --name "<name>"`; the contract is
|
|
`tools/wikitool types describe instruction`.
|
|
|
|
- **Imperative title.** It answers "what does this tell me to do?".
|
|
- **`description` is the retrieval wire.** Write it to match the question an agent would ask
|
|
when it needs this procedure, not as a label for the file.
|
|
- **Frontload.** Self-contained enough for an agent with no prior context: define terms
|
|
inline, do not assume other documents are loaded.
|
|
- **Keep reasoning out of the body.** Cut the explanation of *why* each step exists. If it is
|
|
worth preserving, it is a concept page under `kb/concepts/`, linked from here. Keep only
|
|
enough reasoning to decide edge cases.
|
|
- **State scope boundaries.** When does this *not* apply, and what to do instead.
|
|
|
|
## Instruction duality
|
|
|
|
These files are both content and running system. Changing one changes agent behaviour
|
|
immediately: the edit is live for the next agent that loads the text, with no release step.
|
|
Treat edits as deployments, not documentation updates.
|
|
|
|
The same duality runs the other way. An instruction nothing loads is inert - it deploys to no
|
|
one. `tools/wikitool instructions verify` reports a file here that nothing references, because
|
|
otherwise nothing would - unless it is `manual: true` (see "Two forms, three reference tiers"
|
|
above), where the same duality flips the check: being loadable from somewhere IS the fault.
|
|
|
|
## Single source
|
|
|
|
A rule belongs in exactly one place; everywhere else links to it. This is an authoring rule,
|
|
not a checked one - prose duplication is a judgment call, so it is reviewed during a
|
|
`wiki-lint` pass rather than enforced by a validator.
|
|
|
|
What lives where:
|
|
|
|
| Layer | Owns |
|
|
|-------|------|
|
|
| [AGENTS.md](../AGENTS.md) | Invariants and routing - what must always hold |
|
|
| `instructions/` | How the tooling is *operated* |
|
|
| [kb/CONTRACT.md](../kb/CONTRACT.md) | What the stack enforces about a page, in every instance |
|
|
| `kb/CONVENTIONS.md` + each `COLLECTION.md` | What *this* instance decided about authoring - owned by the instance, shipped only as a `.template` |
|
|
| [types/](../types/type-spec.md) | What a page structurally *is* |
|
|
| [tools/CONTRACT.md](../tools/CONTRACT.md) | What each command does and how it fails |
|
|
|
|
## What does not belong here
|
|
|
|
- Knowledge. A fact about a system is a page under `kb/`.
|
|
- The reasoning behind a procedure - that is a concept page, linked from the instruction.
|
|
- Anything under `.agents/skills/` or `.claude/skills/`: those are generated copies.
|