stack-build and stack-close carry disable-model-invocation, so each phase change is the operator's slash command; no skill offers a mid-session /model or /effort switch. Mode rules and the phase table move to instructions/dev/stack-mode.md, publish and CI waiting to instructions/dev/publish-and-ci.md, the ready definition to issue-tracking.md. Files changed: - AGENTS.md - CHANGES.md - DEVELOPMENT.md - README.md - VERSION - docs/model-and-effort-selection.md - instructions/CONTRACT.md - instructions/dev/commonplace-kb.md - instructions/dev/dev-setup.md - instructions/dev/doc-pull-through.md - instructions/dev/issue-tracking.md - instructions/dev/publish-and-ci.md - instructions/dev/stack-build/SKILL.md - instructions/dev/stack-close/SKILL.md - instructions/dev/stack-dev/SKILL.md - instructions/dev/stack-mode.md - instructions/dev/testing-conventions.md - instructions/dev/version-parts.md - tools/chemenu/commands/git_publish.py - tools/chemenu/tests/test_git_publish.py - tools/chemenu/tests/test_instructions_cmd.py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
27 KiB
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.
Contents
- Two forms, three reference tiers
instructions/migrations/instructions/dev/- Publishing
- Writing an instruction
- A skill's H1 is a name, not an imperative
- A skill's
descriptionspeaks in third person - A skill's name declares its family
- A skill's outbound reference is a plain path, not a link
- Reference depth: bundled files, not repo-wide contracts
- When a skill carries a copy-in checklist
- How much reasoning a step may carry
- Instruction duality
- Single source
- What does not belong here
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.
verifyscans neither when asking whether an instruction is still reachable, which is exactly why the answer means something. Theinstructions/dev/boundary check below asks the opposite question - what would dangle in a distributed instance - and does scan README.md, becausedist exportships it verbatim.
Three kinds of file use the Manual tier today: german-terminology.md, a
vocabulary consulted on demand rather than a procedure; 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.
wikitool new instruction scaffolds none of the three: migrates_to: and migration_kind:
have no schema default: at all, and an ordinary instruction's scaffold no longer materializes
obligation:'s default either - all three are added by hand when a migration document is
written, per migrate-corpus.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, 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),
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.
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?". This binds the flat
instructions/<name>.mdform only - a skill's H1 is a different case, below. descriptionis 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. What this does and does not say about linking a shared contract: below.
- Reasoning earns its place by deciding something. Keep what an agent needs in order to get
this decision right, at the step where it falls; cut the explanation of why the step exists
at all. If that is worth preserving, it is a concept page under
kb/concepts/, linked from here. Where the line runs, and how to test a passage against it: below. - State scope boundaries. When does this not apply, and what to do instead.
- A command block reads the same in every shell. Depending on the harness, an instruction
runs under bash, Git Bash or PowerShell 7. A command in a fenced block is a
tools/wikitoolorgitcall, or the preflight's own call per platform - never syntax only one shell reads: no heredoc, noexport, no$(...)or$VAR, no inlineVAR=value command, no&&, noforloop, nocp,cat >,sha256sum,curlortar. A step that needs one of them gets awikitoolcommand instead, or leaves the file work to the agent's own file tools. Two places are exempt, each with one line per shell: setting the session id (session-setup.md) and downloading the preflight before an instance exists (setup-instance.md step 0). Migration documents underinstructions/migrations/belong to the release they shipped with and are not rewritten. The stack's own instructions are held to this by a test in the origin repository; what an instance writes for itself is its own decision. - Write it in English, and let the agent speak the instance's language. Both rules, and the
line between prose and quoted vocabulary, are stated once in
AGENTS.md § File naming. They are named here because this is the
step where they are obeyed or lost: nothing checks either mechanically, and an instruction
that models a sentence for the user is where the two are easiest to confuse - the model is
written in English, the saying of it follows
kb/CONVENTIONS.md'slanguage:.
A skill's H1 is a name, not an imperative
instructions/<name>/SKILL.md takes a name-shaped H1 matching its name: frontmatter -
# Wiki Ingest, not # Ingest a source file into the wiki. The imperative-title rule is
written for the flat form and stops there.
The heading lies on no retrieval path. What decides whether a skill is picked up is
description, which sits in the agent's context from session start; the body is read only once
the skill is already open, and by then the title has nothing left to decide. Anthropic's
skill-authoring guidance agrees by omission and by example: it normalises name and
description and says nothing about the body's heading, and its own worked examples are noun
phrases (# PDF Processing, # BigQuery Data Analysis). So does the vendored commonplace
corpus, which arrived at the imperative-title rule independently and carves out the same
exception in the same breath - "for promoted skills, the skill name is the title".
That is the whole exception. Everything else in this section binds a SKILL.md exactly as it
binds an instruction.
A skill's description speaks in third person
Anthropic's skill-authoring guidance requires third person in a skill's description, because it
is injected into the system prompt for skill selection and an inconsistent point of view degrades
that selection - "Processes Excel files and generates reports", never "I can help you process..."
or "Process...". This binds every instructions/<name>/SKILL.md in this repo. The flat
instructions/<name>.md form's description (above) is read on demand rather than injected as
system-prompt metadata, so it keeps the imperative/label freedom that form already allows.
Nothing checks this mechanically - tools/wikitool docs verify/instructions verify validate a
description's presence and length, not its grammatical voice - so it holds only as long as each
new skill is written to match the ones around it.
A skill's name declares its family
Three prefixes exist today, each naming the subject domain a skill operates on, not the
distribution boundary it ships behind: wiki- for the knowledge pipeline (wiki-ingest,
wiki-lint, wiki-manage, wiki-query, wiki-status), gtd- for the commitment layer
(gtd-weekly-review - see kb/gtd/COLLECTION.md and docs/knowledge-and-commitment.md for why
that layer is named GTD rather than folded into wiki-), and stack- for the stack's own
development, nested under instructions/dev/ and therefore never present in a distributed
instance (instructions/dev/ above).
Dev-instance-only: the three skills in that family today are stack-dev, stack-build and
stack-close.
A new skill takes the prefix of the family it belongs to, or opens a new one deliberately - never a bare name.
This is a convention, not something the tool enforces: an unprefixed or fourth-family name would compile, publish and pass every check exactly like the three above, so it is written down here for the next session to read before adding one.
A skill's outbound reference is a plain path, not a link
tools/wikitool instructions sync copies each SKILL.md byte for byte into
.agents/skills/<name>/ and .claude/skills/<name>/ (§ Publishing, above) - a different depth
than the source, and without the sibling files a relative link might expect. A markdown link
correct at instructions/<name>/SKILL.md (../session-setup.md, ../../kb/CONTRACT.md)
resolves to a different, usually nonexistent, file once copied: the number of ../ segments
that reaches a target from instructions/ does not reach the same target from
.claude/skills/. Fifty-two of the fifty-eight relative links across the repo's seven skills at
the time broke exactly this way before this rule existed, silently - nothing rendered the copy to
notice, and no check read a link target.
So a SKILL.md never writes an outbound reference as a relative markdown link, correct depth or
not. It names the target as a repo-root-relative plain path instead - `instructions/session-setup.md`, not [session-setup.md](../session-setup.md); `kb/CONTRACT.md` for a
whole file, `kb/CONVENTIONS.md` § Tone for a section rather than an anchored link. The path
survives the copy unchanged because it does not depend on where the reading file sits: an
agent's working directory is the instance root regardless of which published copy it opened, so
the same plain path resolves in the source and in both published copies alike. The cost is that
the reference is no longer clickable from the source file - accepted deliberately, because the
source is not where an agent reads it from; the harness reads the published copy.
tools/wikitool instructions verify enforces the ban mechanically
(check_skill_reference_paths).
This binds only SKILL.md. The flat instructions/<name>.md form - this file included - is
never copied anywhere, so its relative links stay exactly as correct as their ../ count says,
and stay ordinary links; tools/wikitool docs verify (check_reference_targets) resolves those
against the working tree instead of banning the syntax, over the same reference-file scope
tools/wikitool docs toc uses.
Reference depth: bundled files, not repo-wide contracts
Anthropic's skill-authoring guidance asks that reference files stay one level deep from
SKILL.md, because a file reached at the second hop may be previewed rather than read -
head -100 instead of the whole file - leaving the step to run on incomplete information.
That rule governs skill-bundled material: files sitting in instructions/<name>/ beside the
SKILL.md. The guidance's own worked example is a bundle (SKILL.md → REDLINING.md,
OOXML.md), and it says nothing about files outside the skill directory. No skill in this repo
has a bundled file today, so as written the rule currently binds nothing here.
A skill's reference to a repo-wide contract - kb/CONTRACT.md, tools/CONTRACT.md,
instructions/gates.md (written as a plain path per § "A skill's outbound reference is a plain
path, not a link" above; this file is a flat instruction rather than a SKILL.md, so its own
references to the same three files, a few sections up and below, stay ordinary links) - is a
different category, and the two halves of the question have different answers:
- The mechanic is real and directory-independent. A contract reached at the second hop can be read partially exactly as a bundled file would be. Nothing about the path makes it safe.
- The rule is not. Reading its scope wider than it states would attribute a rule to a source that does not carry it - the same move invariant 3 forbids about facts.
So the shared contracts stay shared and stay linked once. AGENTS.md invariant 8 is what put them
there: copying kb/CONTRACT.md into five SKILL.md files is precisely the second copy that
drifts. § Frontload does not ask for that either - it asks that a step be decidable without
prior context, not that every rule the step obeys be restated at it.
What the mechanic does oblige is cheaper than either: a link says what the step needs from the
file it points at. A bare "read X first" leaves a partial read undetectable; naming what is to
be taken from it - the field, the section, the decision - keeps the step decidable even when the
read came up short, and tells the next author which reference is actually load-bearing.
wiki-ingest step 7 is the shape: three contracts linked, each with the clause that says why
this step needs it.
This is a narrower posture than the vendored commonplace corpus takes, which makes outbound
links exceptional in its instruction collection and frontloads the rest. That works for a corpus
whose procedures do not share a contract; here they do, and invariant 8 outranks the preview
risk.
All of the above is a judgment, not a measurement, and it is worth knowing why it cannot be
the second. Whether the mechanic bites here is not something this repo can currently observe:
the L2 trajectory scorers read wikitool calls, gates and publishes, never an agent's file
reads, and no tool hook is wired on the primary harness at all - so a head -100 leaves nothing
to score. The other half of the claim, what ended up in the context window, produces no event
anywhere by construction.
Gitea #72 records what such a test would cost and why it was not bought. (Kept behind a strip marker: the pointer is worth having in the origin repo and resolves nowhere else.)
When a skill carries a copy-in checklist
Anthropic's skill-authoring guidance suggests, for a "particularly complex workflow", a checklist the agent copies into its response and ticks off as it goes. It names no threshold, so this repo sets one - otherwise the skills that carry such a block and the ones that do not read as an accident rather than a decision.
A SKILL.md carries the block when one of its flows runs to eight steps or more and that
flow contains steps whose omission is silent - a judgment call, a field filled by hand, a
cadence check, anything no tool error and no validator would report missing. Both halves are
required. Length alone is not the problem: a long flow of tool calls announces its own gaps,
because the next call fails without the previous one.
Two skills qualify today, and the block names each of their numbered steps once, verbatim:
wiki-ingest, whose flow is long and carries steps that fail silently - ## Not Extracted,
the coverage check, and the lint cadence all skip past with no tool error and no validator to
catch the omission - and wiki-lint, whose flow contains several steps that are pure judgment
calls the same way. The rest do not, and the reason is worth stating so nobody adds one out of
symmetry: every other skill's flow is short enough, and fails loudly enough step to step, that a
reader cannot lose the thread even without a checklist - wiki-manage's two flows, wiki-query,
wiki-status and gtd-weekly-review all clear that bar.
Dev-instance-only: stack-dev, stack-build and stack-close sit under the same threshold,
for the same reason.
None of this is counted by number on purpose: a per-skill step count is a claim about a file this
one does not own, and a claim like that can drift silently the moment the other file changes.
This passage once cited wiki-query at six steps where it had already been seven for a while, and
separately named only five of the eight skills that exist - neither wrong number made any check go
red, because nothing here reads another file's prose. The two-halves test above (length and a
silently-omittable step) is what actually does the work of picking wiki-ingest and wiki-lint
out from the rest; a count was never load-bearing for that test, only decoration for it, and
dropping it removes the one part of this passage that could be wrong without anyone noticing.
The block says that it is to be copied and carried, not read. A checklist read once is the table of contents it replaced.
How much reasoning a step may carry
"Cut the reasoning" and "keep enough to decide an edge case" are one sentence pulling two ways, and the largest instruction in this repo lives in the gap. The line runs here:
| Keep | Cut |
|---|---|
| What an agent must know to get this decision right, at the step where it falls | Why the step exists at all |
| The consequence of the wrong choice, when nothing later catches it | The consequence, when a validator, a gate or a later step catches it |
| Why a plausible-looking default is the wrong answer | Background about the design that produced the field |
Two tests, both cheap:
- Substitution. Delete the passage and read the step again. Does an agent with no prior context still make the same call? If yes, it was background. If it now guesses, it was a decision aid, and it stays - however long it runs.
- Once. A decision aid belongs at the step where the decision falls, and at exactly one such
step (AGENTS.md invariant 8). Where the same decision falls at two steps -
wiki-ingestasks forfidelity/authorityin step 5 and again in step 6 - the reasoning is written at the first and the second carries the instruction plus a pointer, never a second telling.
A passage that survives both is not an exception to the rule. Deciding an edge case is the part the rule keeps; the length it takes to do that is not the measure.
Instruction duality
These files are both content and running system. Changing one changes agent behaviour 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 | Invariants and routing - what must always hold |
instructions/ |
How the tooling is operated |
| 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/ | What a page structurally is |
| 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.