@zenera/cli 1.1.15 → 1.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -129
- package/dist/banner.d.ts +5 -1
- package/dist/banner.js +2 -2
- package/dist/catalog.js +19 -20
- package/dist/command.d.ts +5 -0
- package/dist/commands/check.js +5 -1
- package/dist/commands/init.js +41 -6
- package/dist/commands/run.js +4 -0
- package/dist/engine.js +6 -0
- package/dist/env.d.ts +20 -0
- package/dist/env.js +65 -0
- package/dist/keys.d.ts +2 -0
- package/dist/keys.js +2 -0
- package/dist/main.js +10 -6
- package/dist/sandbox.d.ts +9 -0
- package/dist/sandbox.js +23 -3
- package/dist/scaffold.d.ts +6 -0
- package/dist/scaffold.js +56 -15
- package/dist/validate.js +57 -15
- package/package.json +2 -2
- package/templates/editor/.github/copilot-instructions.md +501 -301
- package/templates/editor/.github/prompts/new-agent.prompt.md +6 -6
- package/templates/editor/.github/prompts/new-skill.prompt.md +7 -7
- package/templates/editor/.github/prompts/review-project.prompt.md +54 -15
- package/templates/editor/.github/prompts/sync-with-spec.prompt.md +229 -42
- package/templates/editor/.github/prompts/update-spec-from-feedback.prompt.md +142 -0
- package/templates/editor/.github/skills/zen-cli/SKILL.md +62 -6
- package/templates/editor/.github/skills/zen-cli/references/check.md +9 -6
- package/templates/editor/.github/skills/zen-cli/references/faker.md +14 -14
- package/templates/editor/.github/skills/zen-cli/references/frame.md +11 -9
- package/templates/editor/.github/skills/zen-cli/references/inspect.md +3 -3
- package/templates/editor/.github/skills/zen-cli/references/keys.md +9 -9
- package/templates/editor/.github/skills/zen-cli/references/memory.md +11 -11
- package/templates/editor/.github/skills/zen-cli/references/models.md +8 -8
- package/templates/editor/.github/skills/zen-cli/references/projects.md +23 -6
- package/templates/editor/.github/skills/zen-cli/references/rag.md +191 -56
- package/templates/editor/.github/skills/zen-cli/references/run.md +3 -3
- package/templates/editor/.github/skills/zen-cli/references/sandbox.md +14 -8
- package/templates/editor/.github/skills/zen-memory/SKILL.md +144 -72
- package/templates/editor/.github/skills/zen-memory/scripts/check-instructions.sh +115 -0
- package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +71 -48
- package/templates/editor/.github/skills/zen-rag-schema/SKILL.md +67 -67
- package/templates/editor/.github/skills/zen-review/SKILL.md +123 -0
- package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +119 -0
- package/templates/editor/.github/skills/zen-review/scripts/review.sh +104 -0
- package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +197 -0
- package/templates/editor/.github/skills/zen-spec-sync/scripts/snapshot.sh +200 -0
- package/templates/editor/.vscode/settings.json +46 -3
- package/templates/parts/memory-terms.yaml.tmpl +16 -0
- package/templates/parts/memory.yaml.tmpl +12 -0
- package/templates/project/SPECIFICATION.md.tmpl +7 -0
- package/templates/project/{INSTRUCTIONS.md → agents/instructions.md} +1 -1
- package/templates/project/agents/memory-instructions.md +278 -0
- package/templates/project/agents.yaml.tmpl +9 -0
- package/templates/project/assets/README.md +1 -1
- package/templates/project/env +22 -0
- package/templates/project/sandbox/Dockerfile.tmpl +3 -0
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
# Copilot instructions
|
|
1
|
+
# Copilot instructions - agent projects
|
|
2
2
|
|
|
3
3
|
> Written by `zen init` into `.github/copilot-instructions.md`, alongside the
|
|
4
4
|
> prompt files and skills in the same tree (§2.4). VS Code loads it
|
|
5
5
|
> automatically for every request in this folder, so it is the standing brief
|
|
6
|
-
> for anyone
|
|
6
|
+
> for anyone - human or agent - editing this project.
|
|
7
7
|
>
|
|
8
8
|
> It describes the Zenera Neo runtime, not this particular project, and `zen`
|
|
9
|
-
> rewrites the whole tree on `init` and `open`
|
|
10
|
-
> conventions in `
|
|
11
|
-
> reference is `docs/agents-yaml.md`, `docs/projects.md` and
|
|
12
|
-
> @zenera/neo repository
|
|
9
|
+
> rewrites the whole tree on `init` and `open` - so put this project's own
|
|
10
|
+
> conventions in `agents/instructions.md`, where they will survive. The
|
|
11
|
+
> runtime's own reference is `docs/agents-yaml.md`, `docs/projects.md` and
|
|
12
|
+
> `DESIGN.md` in the @zenera/neo repository - where they disagree with this
|
|
13
|
+
> file, they win.
|
|
13
14
|
|
|
14
15
|
---
|
|
15
16
|
|
|
16
17
|
## 0. What this repository is
|
|
17
18
|
|
|
18
19
|
This is **not a normal application repository**. There is no application code
|
|
19
|
-
here. It is a folder of declarative artefacts
|
|
20
|
+
here. It is a folder of declarative artefacts - YAML and Markdown - that
|
|
20
21
|
assemble into a running multi-agent system, driven by the `zen` CLI.
|
|
21
22
|
|
|
22
23
|
The centre of gravity is **prose**. Every valuable change here is an edit to a
|
|
@@ -34,6 +35,86 @@ programmed: what an agent knows, which model answers, and which of the tools
|
|
|
34
35
|
6. Every change must still load: `agents.yaml` is validated strictly at load,
|
|
35
36
|
and `zen check` says so before a model is ever called.
|
|
36
37
|
|
|
38
|
+
### 0.1 Copilot is the meta-agent: load the skill for every capability in use
|
|
39
|
+
|
|
40
|
+
Copilot is always the meta-agent that creates and changes target projects. Load
|
|
41
|
+
the editor's `zen-cli` skill before deciding how a target project should run,
|
|
42
|
+
be validated, or use command-line capabilities. It is the command contract; do
|
|
43
|
+
not reconstruct `zen` arguments from memory or call `--help` to discover them.
|
|
44
|
+
|
|
45
|
+
**Every runtime capability a project turns on carries obligations that are not
|
|
46
|
+
visible in `agents.yaml` and that `zen check` does not report.** Before
|
|
47
|
+
reviewing or changing a project, list the capabilities it has turned on and load
|
|
48
|
+
the editor skill for each one - `zen-memory` for a `memory:` block, `zen-rag-
|
|
49
|
+
schema` or `zen-rag-docs` for an index, `zen-cli` always. A capability that is
|
|
50
|
+
already configured and already passing `zen check` is exactly the case that
|
|
51
|
+
looks finished and is not: the obligation lives in the skill, so a review that
|
|
52
|
+
never opens the skill cannot find what is missing. This applies whatever
|
|
53
|
+
prompted the change - a task scoped to `SPECIFICATION.md` does not narrow it.
|
|
54
|
+
|
|
55
|
+
First determine whether the target project uses RAG: a `zen rag` command in an
|
|
56
|
+
agent prompt or skill, RAG tools supplied by its host, or a documentation or
|
|
57
|
+
schema index it is expected to consult all count. When it does, also load the
|
|
58
|
+
`zen-cli` skill's `references/rag.md` before writing the target's instructions.
|
|
59
|
+
|
|
60
|
+
Then create or maintain `agents/skills/rag_search/SKILL.md` in the target
|
|
61
|
+
project. Its description must say when an agent should load it. Its body must
|
|
62
|
+
name the target's actual RAG surface and index location, state which agents may
|
|
63
|
+
use it, and give an executable retrieval procedure:
|
|
64
|
+
|
|
65
|
+
1. Discover document and section names with the exact listing command or tool.
|
|
66
|
+
2. Start with hybrid search for a complete natural-language question.
|
|
67
|
+
3. Narrow by document, section, and content kind before repeatedly rewording a
|
|
68
|
+
query; verify facts with exact lookup or a verbatim read.
|
|
69
|
+
4. Use full-text search for a known literal term, vector search for unknown
|
|
70
|
+
wording, and split text/vector queries only when the exact term is
|
|
71
|
+
distinctive enough to improve the semantic question.
|
|
72
|
+
5. Retrieve a complete section verbatim, or list a table and read its inclusive
|
|
73
|
+
line range when every row is needed.
|
|
74
|
+
|
|
75
|
+
Keep this as runtime guidance, not a pointer to this editor reference: a target
|
|
76
|
+
agent cannot read `.github/` or this project layout while it runs. Include only
|
|
77
|
+
the commands, tools, paths, and corpus-specific constraints that exist in the
|
|
78
|
+
target project. Grant the agents that need CLI RAG the necessary `sandbox:*`
|
|
79
|
+
tool access; give tool-based RAG users the configured RAG tools instead. Run
|
|
80
|
+
`zen check` after adding or changing the skill.
|
|
81
|
+
|
|
82
|
+
Memory has one such obligation and it is absolute: **a project with a `memory:`
|
|
83
|
+
block carries the `zen-memory` skill's `references/memory-instructions.md` in its
|
|
84
|
+
own `agents/memory-instructions.md`.** `zen init` writes both, since it scaffolds
|
|
85
|
+
memory on; what needs watching is a project that deleted the file, or one whose
|
|
86
|
+
copy has fallen behind the reference. Do not read the two files against each
|
|
87
|
+
other - they drift by trailing whitespace inside tables, which is invisible.
|
|
88
|
+
The skill ships the check:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
.github/skills/zen-memory/scripts/check-instructions.sh # the verdict
|
|
92
|
+
.github/skills/zen-memory/scripts/check-instructions.sh fix # copy the reference over
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
It is a copy, never a transcription - the reference ships inside the project,
|
|
96
|
+
because `zen init` and `zen open` write this whole `.github/` tree, so `fix` is
|
|
97
|
+
just `cp` from one to the other.
|
|
98
|
+
|
|
99
|
+
Nothing else tells a running agent how the store works, so without it nothing
|
|
100
|
+
forbids an agent from pointing `grep` or `cat` at `/memory` - a read that
|
|
101
|
+
bypasses the audience mask and serves superseded nodes as current. Keep the copy
|
|
102
|
+
byte-identical and put this project's own memory policy in a second topic file,
|
|
103
|
+
`agents/memory-policy-instructions.md`, which is read straight after it: then the
|
|
104
|
+
copy can be re-run after any `zen init` or `zen open` refreshes the reference,
|
|
105
|
+
without losing anything. Never paraphrase the reference into prose of your own.
|
|
106
|
+
See §5.3.
|
|
107
|
+
|
|
108
|
+
A project with a `SPECIFICATION.md` carries one more: **`.spec-sync/` records
|
|
109
|
+
what the last `/sync-with-spec` pass applied**, and the `zen-spec-sync` skill is
|
|
110
|
+
loaded before running that prompt, whether or not a baseline exists yet. The
|
|
111
|
+
state is committed, because a record only the last machine has is not a record.
|
|
112
|
+
Two things to watch for, neither of which `zen check` reports: a baseline that
|
|
113
|
+
moved without a history entry to match it, which is a pass claiming work it did
|
|
114
|
+
not do, and a `.spec-sync/baseline/` whose `manifest.txt` no longer matches its
|
|
115
|
+
files. Both are reported, not quietly repaired - rewriting the manifest asserts
|
|
116
|
+
a pass that never happened.
|
|
117
|
+
|
|
37
118
|
---
|
|
38
119
|
|
|
39
120
|
## 1. Mental model
|
|
@@ -42,15 +123,16 @@ programmed: what an agent knows, which model answers, and which of the tools
|
|
|
42
123
|
|
|
43
124
|
An agent is four things and nothing more:
|
|
44
125
|
|
|
45
|
-
| Part | Where it lives
|
|
46
|
-
| --------------- |
|
|
47
|
-
| **Instruction** | `
|
|
48
|
-
| **Model** | `agents.yaml` → `model:`
|
|
49
|
-
| **Tools** | `agents.yaml` → `tools:`
|
|
50
|
-
| **Knowledge** | `agents/skills/*` + memory
|
|
126
|
+
| Part | Where it lives | What it decides |
|
|
127
|
+
| --------------- | ------------------------------------------------------ | ----------------------------- |
|
|
128
|
+
| **Instruction** | `agents/*instructions.md` + `agents/prompts/<name>.md` | How it behaves |
|
|
129
|
+
| **Model** | `agents.yaml` → `model:` | How well and how expensively |
|
|
130
|
+
| **Tools** | `agents.yaml` → `tools:` | What it can _do_ |
|
|
131
|
+
| **Knowledge** | `agents/skills/*` + memory | What it can _know_, on demand |
|
|
132
|
+
|
|
133
|
+
Plus two relations: **handoffs** - which other agents it may transfer control to
|
|
51
134
|
|
|
52
|
-
|
|
53
|
-
— and **fork** — whether it may split into parallel branches at all (§6.4).
|
|
135
|
+
- and **fork** - whether it may split into parallel branches at all (§6.4).
|
|
54
136
|
|
|
55
137
|
There is no hidden orchestration layer. If behaviour is wrong, one of those six
|
|
56
138
|
things is wrong.
|
|
@@ -81,7 +163,7 @@ is deciding **what is in that sequence and in what order**.
|
|
|
81
163
|
```
|
|
82
164
|
┌─────────────────────────────────────────────┐
|
|
83
165
|
│ tool schemas fixed at load │ ← stable, cacheable
|
|
84
|
-
│
|
|
166
|
+
│ agents/*instructions shared by all agents │ ← stable, cacheable
|
|
85
167
|
│ agent prompt this agent's brief │ ← stable per agent
|
|
86
168
|
│ skill index names + descriptions │ ← stable per agent
|
|
87
169
|
│ preloaded skills activated turn 0 │ ← stable, in the cached prefix
|
|
@@ -93,8 +175,8 @@ is deciding **what is in that sequence and in what order**.
|
|
|
93
175
|
Two consequences that drive nearly every design rule in this document:
|
|
94
176
|
|
|
95
177
|
- **Stable prefix = cache hit.** Providers cache by prefix. Anything appended
|
|
96
|
-
mid-run _after_ the first reply
|
|
97
|
-
activation
|
|
178
|
+
mid-run _after_ the first reply - a tool schema, an instruction, a late skill
|
|
179
|
+
activation - invalidates the cache from that point on. This is why tool schemas
|
|
98
180
|
are fixed at load and skill-owned tools are declared from turn 0 and merely
|
|
99
181
|
_gated_ at call time.
|
|
100
182
|
- **Everything in the prefix is paid for on every call.** A 4 000-token prompt
|
|
@@ -110,7 +192,7 @@ Two consequences that drive nearly every design rule in this document:
|
|
|
110
192
|
a number.
|
|
111
193
|
- Numbers come from tools; the model narrates, it does not compute.
|
|
112
194
|
- The failure mode of every instruction is stated ("if X is absent, say so and
|
|
113
|
-
stop")
|
|
195
|
+
stop") - an unstated failure mode is an invented one.
|
|
114
196
|
|
|
115
197
|
---
|
|
116
198
|
|
|
@@ -120,14 +202,16 @@ Two consequences that drive nearly every design rule in this document:
|
|
|
120
202
|
|
|
121
203
|
```
|
|
122
204
|
my-project/
|
|
123
|
-
├── .github/ the editor's brief
|
|
205
|
+
├── .github/ the editor's brief - §2.4
|
|
124
206
|
│ ├── copilot-instructions.md this file
|
|
125
207
|
│ ├── prompts/*.prompt.md tasks you invoke by name
|
|
126
208
|
│ └── skills/*/SKILL.md reference the editor loads on demand
|
|
127
|
-
├── .env
|
|
128
|
-
├──
|
|
209
|
+
├── .env this project's environment - NEVER committed
|
|
210
|
+
├── .spec-sync/ what the last /sync-with-spec applied - §0.1
|
|
129
211
|
├── agents.yaml who exists, what they may reach for
|
|
130
212
|
├── agents/
|
|
213
|
+
│ ├── instructions.md house rules, prepended to every agent
|
|
214
|
+
│ ├── memory-instructions.md more of them - any `<topic>-instructions.md`
|
|
131
215
|
│ ├── prompts/
|
|
132
216
|
│ │ ├── intake.md one agent's own brief
|
|
133
217
|
│ │ └── adjuster.md
|
|
@@ -139,10 +223,10 @@ my-project/
|
|
|
139
223
|
│ │ └── SKILL.md
|
|
140
224
|
│ └── refund_policy/
|
|
141
225
|
│ ├── SKILL.md
|
|
142
|
-
│ └── scripts/calculate.py run at /skills/refund_policy/scripts
|
|
226
|
+
│ └── scripts/calculate.py run at /skills/refund_policy/scripts - §3.4.1
|
|
143
227
|
├── assets/ reference material, read-only at /assets
|
|
144
|
-
├── memory/ the graph agents remember into
|
|
145
|
-
├── sandbox/Dockerfile the image commands run in
|
|
228
|
+
├── memory/ the graph agents remember into - §5.3
|
|
229
|
+
├── sandbox/Dockerfile the image commands run in - §3.7
|
|
146
230
|
└── sessions/ run state, whatever the agent wrote
|
|
147
231
|
```
|
|
148
232
|
|
|
@@ -150,28 +234,33 @@ Only `agents.yaml` is required, and only `agents:` is required inside it.
|
|
|
150
234
|
The config is found by name, in order: `agents.yaml`, `agents.yml`,
|
|
151
235
|
`agents/agents.yaml`, `agents/agents.yml`.
|
|
152
236
|
|
|
237
|
+
The house rules are `agents/instructions.md` plus every
|
|
238
|
+
`agents/<topic>-instructions.md`, read in filename order and prepended to every
|
|
239
|
+
agent (§3.2). A project written before the move keeps its `INSTRUCTIONS.md` in
|
|
240
|
+
the root; that still loads, and `zen check` says to move it.
|
|
241
|
+
|
|
153
242
|
### 2.2 Variants
|
|
154
243
|
|
|
155
|
-
**Single agent, knowledge-heavy**
|
|
244
|
+
**Single agent, knowledge-heavy** - the most under-used shape. One agent, one
|
|
156
245
|
prompt, a large skill catalog. Prefer this until routing is genuinely needed.
|
|
157
246
|
|
|
158
247
|
```
|
|
159
|
-
|
|
248
|
+
agents.yaml · agents/instructions.md · agents/prompts/assistant.md · agents/skills/** (20 skills)
|
|
160
249
|
```
|
|
161
250
|
|
|
162
|
-
**Router + specialists**
|
|
251
|
+
**Router + specialists** - a cheap intake agent that classifies and hands off.
|
|
163
252
|
|
|
164
253
|
```
|
|
165
254
|
agents/prompts/{intake,billing,technical,escalation}.md
|
|
166
255
|
```
|
|
167
256
|
|
|
168
|
-
**Pipeline**
|
|
257
|
+
**Pipeline** - fixed stages, each handing to the next; the last one answers.
|
|
169
258
|
|
|
170
259
|
```
|
|
171
260
|
research → draft → review
|
|
172
261
|
```
|
|
173
262
|
|
|
174
|
-
**Shared skills across projects**
|
|
263
|
+
**Shared skills across projects** - `skills:` accepts a list, merged into one
|
|
175
264
|
catalog:
|
|
176
265
|
|
|
177
266
|
```yaml
|
|
@@ -181,7 +270,7 @@ skills:
|
|
|
181
270
|
```
|
|
182
271
|
|
|
183
272
|
Paths may not escape the project root unless the root is set to the common
|
|
184
|
-
ancestor. Several directories become **one** provider (id `project`)
|
|
273
|
+
ancestor. Several directories become **one** provider (id `project`) - "which
|
|
185
274
|
folder is this skill in?" is not a question an author should have to answer.
|
|
186
275
|
|
|
187
276
|
### 2.3 Naming rules (enforced)
|
|
@@ -201,29 +290,30 @@ dots.
|
|
|
201
290
|
There are two sets of instructions in this repository and they are not for the
|
|
202
291
|
same reader. Keeping them apart is the single easiest thing to get wrong.
|
|
203
292
|
|
|
204
|
-
| Tree
|
|
205
|
-
|
|
|
206
|
-
| `
|
|
207
|
-
| `.github/`
|
|
293
|
+
| Tree | Read by | About |
|
|
294
|
+
| ------------------------ | ------------------------------------------ | ----------------------------------- |
|
|
295
|
+
| `agents.yaml`, `agents/` | the **project's** agents, at run time | the domain this system works in |
|
|
296
|
+
| `.github/` | the **editor's** assistant, while you edit | how a project of this kind is built |
|
|
208
297
|
|
|
209
298
|
The `.github/` tree follows the same progressive-disclosure discipline the
|
|
210
|
-
agents do, for the same reason
|
|
299
|
+
agents do, for the same reason - it is a prefix somebody pays for:
|
|
211
300
|
|
|
212
301
|
- **`copilot-instructions.md`** is always on. Everything in it is loaded for
|
|
213
302
|
every request in this folder, so it holds only what is true of every task.
|
|
214
303
|
- **`.github/skills/<name>/SKILL.md`** is reference the editor loads when its
|
|
215
|
-
`description` matches what you asked. Put long, occasional material here
|
|
216
|
-
a command surface, a vendor's quirks, a format spec
|
|
304
|
+
`description` matches what you asked. Put long, occasional material here -
|
|
305
|
+
a command surface, a vendor's quirks, a format spec - not in the file above.
|
|
217
306
|
The `description` is the routing key; §3.4 applies to these as much as to the
|
|
218
307
|
project's own skills.
|
|
219
308
|
- **`.github/prompts/<name>.prompt.md`** is a task you invoke by name (`/name`),
|
|
220
309
|
with `mode: agent` and a `description` in its frontmatter. Write one when a
|
|
221
|
-
job is done repeatedly and has a right order
|
|
310
|
+
job is done repeatedly and has a right order - adding an agent, adding a
|
|
222
311
|
skill, reviewing before a commit.
|
|
223
312
|
|
|
224
313
|
`zen init` and `zen open` rewrite this whole tree from the version of `zen` in
|
|
225
314
|
hand, so **edits inside `.github/` do not survive**. Project-specific conventions
|
|
226
|
-
belong in `
|
|
315
|
+
belong in `agents/instructions.md` and the agent prompts, which are never
|
|
316
|
+
overwritten.
|
|
227
317
|
|
|
228
318
|
### 2.5 What a running agent can actually see
|
|
229
319
|
|
|
@@ -232,24 +322,24 @@ it. Its entire world is the tools it was granted (§3.5) and four paths:
|
|
|
232
322
|
|
|
233
323
|
| Path | Is | Access |
|
|
234
324
|
| ------------ | ----------------------------------------- | ------------------------------------------------------ |
|
|
235
|
-
| `/workspace` | the session's workspace
|
|
236
|
-
| `/assets` | `assets/`, reference material
|
|
237
|
-
| `/skills` | the whole skill catalog
|
|
238
|
-
| `/memory` | files carried by remembered nodes
|
|
325
|
+
| `/workspace` | the session's workspace - the work itself | read/write; `zen run --read-only` withholds the writes |
|
|
326
|
+
| `/assets` | `assets/`, reference material - §3.11 | read-only |
|
|
327
|
+
| `/skills` | the whole skill catalog - §3.4.1 | read-only |
|
|
328
|
+
| `/memory` | files carried by remembered nodes - §5.3 | read-only, and only when `memory:` is configured |
|
|
239
329
|
|
|
240
330
|
Commands run inside the container `sandbox:` describes (§3.7): the same
|
|
241
331
|
`/workspace`, and whatever the image ships.
|
|
242
332
|
|
|
243
|
-
Everything else in the project directory is **invisible at run time**
|
|
244
|
-
`agents.yaml`, `
|
|
245
|
-
name, `sandbox/Dockerfile`, `.env`, `.github/`, `sessions/`, and the
|
|
246
|
-
root itself. `zen` reads them to build the run; they are never a path an
|
|
247
|
-
can open.
|
|
333
|
+
Everything else in the project directory is **invisible at run time** -
|
|
334
|
+
`agents.yaml`, `agents/instructions.md`, `agents/prompts/`, `agents/skills/`
|
|
335
|
+
under that name, `sandbox/Dockerfile`, `.env`, `.github/`, `sessions/`, and the
|
|
336
|
+
project root itself. `zen` reads them to build the run; they are never a path an
|
|
337
|
+
agent can open.
|
|
248
338
|
|
|
249
339
|
Two consequences:
|
|
250
340
|
|
|
251
341
|
- **The workspace is not the project.** It defaults to the session's own empty
|
|
252
|
-
folder, and is usually some other repository entirely
|
|
342
|
+
folder, and is usually some other repository entirely - `--workspace`, or the
|
|
253
343
|
directory the user was standing in. A prompt saying "read `agents.yaml`" or
|
|
254
344
|
"the rules are in `agents/skills/`" names a file that is not there.
|
|
255
345
|
- **Write the mount path, never the repository path.**
|
|
@@ -259,46 +349,50 @@ Two consequences:
|
|
|
259
349
|
the one place the file is not.
|
|
260
350
|
|
|
261
351
|
An agent that must know something about its own configuration is **told it in
|
|
262
|
-
prose**, in the prompt
|
|
352
|
+
prose**, in the prompt - never sent to read the file that configured it.
|
|
263
353
|
|
|
264
|
-
#### Checklist
|
|
354
|
+
#### Checklist - no path an agent cannot open
|
|
265
355
|
|
|
266
|
-
Run this over `
|
|
267
|
-
`agents/skills/*/SKILL.md` after editing any of them. Every path-shaped
|
|
268
|
-
those files is read by a model as an instruction to open something:
|
|
356
|
+
Run this over every `agents/*instructions.md`, every `agents/prompts/*.md` and
|
|
357
|
+
every `agents/skills/*/SKILL.md` after editing any of them. Every path-shaped
|
|
358
|
+
token in those files is read by a model as an instruction to open something:
|
|
269
359
|
|
|
270
360
|
- [ ] Every absolute path begins `/workspace`, `/assets`, `/skills` or
|
|
271
361
|
`/memory`. Nothing else exists.
|
|
272
|
-
- [ ] No project file is named: `agents.yaml`, `
|
|
362
|
+
- [ ] No project file is named: `agents.yaml`, `agents/instructions.md`,
|
|
273
363
|
`agents/prompts/…`, `agents/skills/…`, `sandbox/Dockerfile`, `.env`,
|
|
274
364
|
`.github/…`, `sessions/…`, `SPECIFICATION.md`, `README.md`, `docs/…`.
|
|
275
365
|
- [ ] Nothing escapes a mount: no `../`, no `~`, no host path (`/Users/…`,
|
|
276
366
|
`/home/<someone>`), no `/etc`, `/var` or `/tmp` expected to hold anything
|
|
277
367
|
the project put there.
|
|
278
368
|
- [ ] Each `/assets/…` and `/skills/<name>/…` path names a file that is actually
|
|
279
|
-
committed there
|
|
369
|
+
committed there - `zen check` lists what every skill folder ships (§3.4.1).
|
|
280
370
|
- [ ] A skill referring to its own sibling file writes the full
|
|
281
371
|
`/skills/<name>/` prefix, not `./rates.csv`: the body is rendered into a
|
|
282
372
|
prompt, not executed from its folder.
|
|
283
373
|
- [ ] `/memory` is named only in prompts for agents that have `memory:` (§5.3),
|
|
284
374
|
and `/assets` only where an `assets/` directory exists.
|
|
285
|
-
- [ ] The agent holding the prompt has the tool the path implies
|
|
375
|
+
- [ ] The agent holding the prompt has the tool the path implies - `read_file`
|
|
286
376
|
to open one, `sandbox:*` to run one (§3.4.1).
|
|
287
377
|
- [ ] Every instruction that opens a file states what to do when it is not there
|
|
288
378
|
(§4.2, rule 6). Workspaces differ between runs; absence is normal.
|
|
289
379
|
- [ ] Relative paths appear only where `/workspace`-relative is what was meant.
|
|
290
380
|
|
|
291
|
-
Finding the candidates, from the project root
|
|
381
|
+
Finding the candidates, from the project root - every path-shaped token in the
|
|
292
382
|
three trees, minus the ones under a mount:
|
|
293
383
|
|
|
294
384
|
```sh
|
|
295
|
-
|
|
296
|
-
INSTRUCTIONS.md agents/prompts agents/skills \
|
|
297
|
-
| sort -u | grep -Ev '^/(workspace|assets|skills|memory)(/|$)'
|
|
385
|
+
.github/skills/zen-review/scripts/check-paths.sh
|
|
298
386
|
```
|
|
299
387
|
|
|
300
|
-
|
|
301
|
-
|
|
388
|
+
It prints each candidate with the file and line it is on, and exits non-zero
|
|
389
|
+
while any remain. Everything it prints is either a path the agent cannot open or
|
|
390
|
+
noise the script has not learned yet - a url, a `path/to` in a sentence, an
|
|
391
|
+
`and/or` - and there is no third case. It strips the noise it does know about,
|
|
392
|
+
so a candidate that survives is one to explain or fix rather than one to skim
|
|
393
|
+
past. `check-paths.sh raw` is the sweep with nothing filtered, for when the
|
|
394
|
+
filter itself is the suspect. The `zen-review` skill says what it covers and
|
|
395
|
+
what it cannot.
|
|
302
396
|
|
|
303
397
|
---
|
|
304
398
|
|
|
@@ -307,20 +401,20 @@ sentence, `and/or`) or a path the agent cannot open. There is no third case.
|
|
|
307
401
|
### 3.1 `agents.yaml`
|
|
308
402
|
|
|
309
403
|
The schema is **strict**: an unknown key is a load error, not a value silently
|
|
310
|
-
ignored. Errors name the path
|
|
404
|
+
ignored. Errors name the path - `agents.yaml: agents[1].skills.discovery - …`.
|
|
311
405
|
|
|
312
406
|
```yaml
|
|
313
407
|
version: 1 # schema version, defaults to 1
|
|
314
408
|
default: intake # entry agent; wins over any `default: true`
|
|
315
409
|
|
|
316
410
|
providers: {} # named connections (credentials + endpoint)
|
|
317
|
-
provider: openai # the provider an unprefixed model id belongs to
|
|
411
|
+
provider: openai # the provider an unprefixed model id belongs to - §7.3
|
|
318
412
|
models: {} # named model configurations
|
|
319
413
|
model: fast # fallback for agents that do not pin their own
|
|
320
|
-
embeddings: {} # named vectorisers
|
|
414
|
+
embeddings: {} # named vectorisers - §3.1.1
|
|
321
415
|
embedding: small # the one `AgentProject.embedder()` returns when asked for no name
|
|
322
416
|
skills: agents/skills # one directory, or a list
|
|
323
|
-
assets: assets # reference material, read-only at /assets
|
|
417
|
+
assets: assets # reference material, read-only at /assets - §3.11
|
|
324
418
|
|
|
325
419
|
agents: # the only required key; at least one entry
|
|
326
420
|
- name: intake
|
|
@@ -339,13 +433,13 @@ agents: # the only required key; at least one entry
|
|
|
339
433
|
| `description` | What a sibling's `transfer_to_<name>` tool tells the model. Write it _for the model_ |
|
|
340
434
|
| `system` | Prompt path, relative to root. Defaults to `agents/prompts/<name>.md` if present |
|
|
341
435
|
| `model` | A `models:` alias or shorthand. Falls back to top-level `model:` |
|
|
342
|
-
| `tools` | Selectors over the tools `zen run` provides
|
|
436
|
+
| `tools` | Selectors over the tools `zen run` provides - see §3.6 |
|
|
343
437
|
| `handoffs` | Agent names this one may transfer to. Bare strings; no per-edge config |
|
|
344
|
-
| `skills` | Skill binding
|
|
345
|
-
| `fork` | `true`, or `{ agents, maxBranches }`
|
|
438
|
+
| `skills` | Skill binding - see §5.2 |
|
|
439
|
+
| `fork` | `true`, or `{ agents, maxBranches }` - opt-in to parallel branches; see §6.4 |
|
|
346
440
|
| `default` | `true` marks the entry point when no top-level `default:` |
|
|
347
441
|
|
|
348
|
-
**Providers**
|
|
442
|
+
**Providers** - a provider is a _connection_, not a model. One client is built
|
|
349
443
|
per name and shared, so five agents on one key open one connection pool.
|
|
350
444
|
|
|
351
445
|
| Field | Meaning |
|
|
@@ -353,20 +447,20 @@ per name and shared, so five agents on one key open one connection pool.
|
|
|
353
447
|
| `kind` | `openai` \| `google` \| `vertex` \| `anthropic` \| `openrouter` \| `openai-compatible` (default `openai`) |
|
|
354
448
|
| `apiKey` / `apiKeyEnv` | Literal, `${VAR}`, or the name of the env var holding it |
|
|
355
449
|
| `baseURL` / `baseURLEnv` | For gateways and compatible endpoints |
|
|
356
|
-
| `project` / `location` | **vertex only**
|
|
450
|
+
| `project` / `location` | **vertex only** - GCP project id and region (or `global`) |
|
|
357
451
|
| `headers` | Sent on every request: routing, attribution, api versions |
|
|
358
452
|
| `timeoutMs`, `maxRetries` | Per-request timeout and retry count |
|
|
359
453
|
|
|
360
454
|
`openai`, `google`, `vertex`, `anthropic`, `openrouter` and `openai-compatible`
|
|
361
455
|
are usable as provider _names_ with no declaration at all. Declare a
|
|
362
|
-
`providers:` entry only when it says something the default does not
|
|
456
|
+
`providers:` entry only when it says something the default does not - a second
|
|
363
457
|
key, a region, a base url.
|
|
364
458
|
|
|
365
459
|
`${VAR}` and `${VAR:-fallback}` expand from the environment, compose inside
|
|
366
460
|
longer values (`https://${GATEWAY}/v1`), and are **lazy**: a declared-but-unused
|
|
367
461
|
provider with a missing key does not fail loading.
|
|
368
462
|
|
|
369
|
-
**Errors caught at load**
|
|
463
|
+
**Errors caught at load** - rely on these instead of defensive checks:
|
|
370
464
|
|
|
371
465
|
- any unknown key; any name breaking the pattern
|
|
372
466
|
- `models.<alias>.provider` naming an undeclared provider
|
|
@@ -375,27 +469,27 @@ provider with a missing key does not fail loading.
|
|
|
375
469
|
- `agents[].handoffs` naming an unknown agent, or the agent itself
|
|
376
470
|
- `agents[].skills.provider` / `.allow` / `.preload` naming something absent
|
|
377
471
|
- a `preload:` entry missing from `allow:`
|
|
378
|
-
- `agents[].fork.agents` naming an unknown agent, or being empty; `maxBranches` below
|
|
472
|
+
- `agents[].fork.agents` naming an unknown agent, or being empty; `maxBranches` below 1
|
|
379
473
|
- `system:` pointing at a missing file, or outside the project root
|
|
380
474
|
|
|
381
|
-
**Not caught at load**
|
|
475
|
+
**Not caught at load** - a model id whose prefix is missing and so resolves to the
|
|
382
476
|
wrong provider (§7.3), and any combination of knobs the vendor rejects at request
|
|
383
477
|
time, such as OpenAI reasoning on chat completions (§7.6). Both surface on the
|
|
384
478
|
first call, so read §7 before writing a `models:` entry.
|
|
385
479
|
|
|
386
|
-
**Caught by `zen check`, not by the loader**
|
|
480
|
+
**Caught by `zen check`, not by the loader** - a handoff with no path back
|
|
387
481
|
(`handoff.one-way`, §6.3). The project runs; it just cannot return.
|
|
388
482
|
|
|
389
|
-
**Comments in `agents.yaml`**
|
|
483
|
+
**Comments in `agents.yaml`** - this file is the architecture diagram of the
|
|
390
484
|
project, and its comments are read by whoever has to change it next. Write them
|
|
391
485
|
at that level:
|
|
392
486
|
|
|
393
487
|
- Say **what a block is for** and **why it exists**: what this agent owns, why
|
|
394
488
|
this one is on the deep tier, why this handoff edge is there.
|
|
395
|
-
- Keep them **short**
|
|
489
|
+
- Keep them **short** - one line above a block, a few words at the end of a line.
|
|
396
490
|
A comment longer than the thing it describes is a design doc in the wrong file.
|
|
397
491
|
- Do **not** restate the runtime. How skills are discovered, how the trajectory
|
|
398
|
-
is appended, how prompt caching works, what the loader validates
|
|
492
|
+
is appended, how prompt caching works, what the loader validates - none of that
|
|
399
493
|
belongs here. It is documented in this file and in `docs/agents-yaml.md`.
|
|
400
494
|
- Do **not** restate the key. `# the model this agent uses` above `model:` is
|
|
401
495
|
noise; `# cheap: it only classifies` is not.
|
|
@@ -404,7 +498,7 @@ at that level:
|
|
|
404
498
|
```yaml
|
|
405
499
|
# Intake classifies and routes; it never answers.
|
|
406
500
|
- name: intake
|
|
407
|
-
model: router # cheap tier
|
|
501
|
+
model: router # cheap tier - one sentence in, one handoff out
|
|
408
502
|
handoffs: [adjuster, escalation]
|
|
409
503
|
```
|
|
410
504
|
|
|
@@ -432,12 +526,12 @@ embedding: small
|
|
|
432
526
|
| Field | Meaning |
|
|
433
527
|
| ------------------------ | ------------------------------------------------------------------------ |
|
|
434
528
|
| `provider` | A `providers:` name or a built-in kind. Defaults to the default provider |
|
|
435
|
-
| `model` | **Required.** The bare id
|
|
529
|
+
| `model` | **Required.** The bare id - the object form never re-parses a shorthand |
|
|
436
530
|
| `dimensions` | Truncate to this width, where the model supports it |
|
|
437
531
|
| `apiKey` / `baseURL` / … | The same credential fields a provider takes, for a one-off connection |
|
|
438
|
-
| `title` | **gemini only**
|
|
439
|
-
| `maxBatch` | **gemini only**
|
|
440
|
-
| `routing` | **openrouter only**
|
|
532
|
+
| `title` | **gemini only** - a document title the retrieval task type weighs |
|
|
533
|
+
| `maxBatch` | **gemini only** - texts per request; see below |
|
|
534
|
+
| `routing` | **openrouter only** - which upstream provider serves the request |
|
|
441
535
|
|
|
442
536
|
Four things differ from `models:` and are worth knowing before you write one:
|
|
443
537
|
|
|
@@ -445,14 +539,14 @@ Four things differ from `models:` and are worth knowing before you write one:
|
|
|
445
539
|
api means nothing on any protocol. The shorthand is `[provider:]model`.
|
|
446
540
|
- **`kind: anthropic` has no embeddings API at all.** Anthropic publishes none
|
|
447
541
|
and points at third parties; an `embeddings:` entry on an Anthropic provider
|
|
448
|
-
fails at load. Use another provider
|
|
542
|
+
fails at load. Use another provider - the connection need not be the one the
|
|
449
543
|
agents talk through.
|
|
450
544
|
- **Not a per-agent key.** Nothing in the runtime consumes a vectoriser yet, so
|
|
451
545
|
there is no `agents[].embedding:`. A TypeScript host reaches one with
|
|
452
546
|
`project.embedder()` for the default, or `project.embedder('large')` by name.
|
|
453
547
|
- **Vectors come back unit length**, so cosine and dot product agree. This is a
|
|
454
548
|
guarantee of the runtime, not of the vendor: truncating with `dimensions:` is a
|
|
455
|
-
raw slice and only some models rescale afterwards
|
|
549
|
+
raw slice and only some models rescale afterwards - `gemini-embedding-2` does,
|
|
456
550
|
`gemini-embedding-001` returns |v| ≈ 0.58 at 768 of its 3072. Pass
|
|
457
551
|
`normalize: false` on a call to see what the model actually said.
|
|
458
552
|
|
|
@@ -464,14 +558,26 @@ hide that. `maxBatch` therefore defaults to `1`; raise it only for a
|
|
|
464
558
|
`zen check` reports every declared embedding beside the models, with the
|
|
465
559
|
credential each one would need.
|
|
466
560
|
|
|
467
|
-
### 3.2 `
|
|
561
|
+
### 3.2 `agents/instructions.md` and friends
|
|
468
562
|
|
|
469
|
-
House rules, read **once** and prepended to every agent's system prompt.
|
|
470
|
-
the stable head of the cached prefix, so
|
|
471
|
-
deliberately not `AGENTS.md`
|
|
563
|
+
House rules, read **once** and prepended to every agent's system prompt. They are
|
|
564
|
+
the stable head of the cached prefix, so they should change rarely. The name is
|
|
565
|
+
deliberately not `AGENTS.md` - that one belongs to the coding assistant reading
|
|
472
566
|
this file, and these rules address the project's own agents.
|
|
473
567
|
|
|
474
|
-
|
|
568
|
+
There may be more than one. `agents/instructions.md` is the base; every
|
|
569
|
+
`agents/<topic>-instructions.md` is read after it, in filename order, and every
|
|
570
|
+
agent gets all of them. Each arrives as its own block tagged with the file it
|
|
571
|
+
came from - `<house_rules src="agents/memory-instructions.md">` - so the model
|
|
572
|
+
can tell one document from the next.
|
|
573
|
+
|
|
574
|
+
Split a topic out when it is true for every agent but belongs to **one
|
|
575
|
+
capability**: the memory usage rules (§5.3) are the standard case - they arrive
|
|
576
|
+
when `memory:` is turned on and are deleted when it is turned off, which is a
|
|
577
|
+
whole file rather than a section somebody has to find. Do not split by author,
|
|
578
|
+
by date, or to keep a file short; that is what a skill is for.
|
|
579
|
+
|
|
580
|
+
Put in them only what is true for **every** agent:
|
|
475
581
|
|
|
476
582
|
- identity and domain ("You work the property claims desk")
|
|
477
583
|
- non-negotiable prohibitions (regulatory, legal, safety)
|
|
@@ -482,11 +588,11 @@ Do **not** put here: anything one agent needs and another does not; anything tha
|
|
|
482
588
|
changes weekly; long reference data (that is a skill).
|
|
483
589
|
|
|
484
590
|
**When writing it, remember what it is: a prefix on every agent's system prompt.**
|
|
485
|
-
It is not a README and not a design document
|
|
591
|
+
It is not a README and not a design document - every line is paid for on every
|
|
486
592
|
call, by every agent, and each one is an instruction the model will try to
|
|
487
593
|
follow. So:
|
|
488
594
|
|
|
489
|
-
- It is a **prompt**: §4.2 applies in full
|
|
595
|
+
- It is a **prompt**: §4.2 applies in full - second person, imperative, no
|
|
490
596
|
hedging, no meta-talk about the runtime, failure paths stated.
|
|
491
597
|
- Carry the **shared model of the project**: what this system is, what the agents
|
|
492
598
|
are collectively for, how the work flows between them, and the vocabulary and
|
|
@@ -498,13 +604,13 @@ follow. So:
|
|
|
498
604
|
- Anything only one agent needs goes in that agent's prompt instead. If you find
|
|
499
605
|
yourself writing "if you are the router…", you are in the wrong file.
|
|
500
606
|
|
|
501
|
-
Target 20–60 lines. If
|
|
607
|
+
Target 20–60 lines each. If one exceeds ~100, split the stable half out into a
|
|
502
608
|
preloaded skill.
|
|
503
609
|
|
|
504
610
|
### 3.3 `agents/prompts/<name>.md`
|
|
505
611
|
|
|
506
612
|
Plain Markdown, no frontmatter. This is the agent's _job description_, appended
|
|
507
|
-
after
|
|
613
|
+
after the house rules.
|
|
508
614
|
|
|
509
615
|
Structure that works:
|
|
510
616
|
|
|
@@ -513,7 +619,7 @@ Structure that works:
|
|
|
513
619
|
|
|
514
620
|
<what it does, as a short numbered procedure or 3–5 rules>
|
|
515
621
|
|
|
516
|
-
<what it must NOT do
|
|
622
|
+
<what it must NOT do - especially the neighbouring agent's job>
|
|
517
623
|
|
|
518
624
|
<how to finish: the shape of the answer, or which handoff ends the turn>
|
|
519
625
|
```
|
|
@@ -530,7 +636,7 @@ Your job is small and you should finish it fast:
|
|
|
530
636
|
and see what it covers.
|
|
531
637
|
3. Hand the case to the `adjuster` agent, which owns the peril policies.
|
|
532
638
|
|
|
533
|
-
Do not quote coverage rules yourself
|
|
639
|
+
Do not quote coverage rules yourself - you do not have them. Do not ask the
|
|
534
640
|
claimant for anything the message already contains.
|
|
535
641
|
```
|
|
536
642
|
|
|
@@ -538,9 +644,9 @@ Note what makes it work: it is 9 lines; it names the tool and the handoff
|
|
|
538
644
|
literally; it states the boundary ("you do not have them") with the _reason_; it
|
|
539
645
|
forbids the specific failure that agent actually exhibits.
|
|
540
646
|
|
|
541
|
-
Write it as a prompt, under the rules in §4.2
|
|
542
|
-
tells one agent what to do; it never explains the system (
|
|
543
|
-
already did, §3.2), never repeats
|
|
647
|
+
Write it as a prompt, under the rules in §4.2 - **instructive and concise**. It
|
|
648
|
+
tells one agent what to do; it never explains the system (the house rules
|
|
649
|
+
already did, §3.2), never repeats one of them, and never describes the runtime.
|
|
544
650
|
Every line should be an instruction the model can act on or a boundary it can
|
|
545
651
|
check itself against.
|
|
546
652
|
|
|
@@ -549,8 +655,8 @@ yet.
|
|
|
549
655
|
|
|
550
656
|
### 3.4 Skills
|
|
551
657
|
|
|
552
|
-
A skill is curated, reusable instruction content
|
|
553
|
-
needs
|
|
658
|
+
A skill is curated, reusable instruction content - plus the files and tools it
|
|
659
|
+
needs - loaded **on demand** instead of permanently occupying the system prompt.
|
|
554
660
|
One layout, and only one:
|
|
555
661
|
|
|
556
662
|
```
|
|
@@ -564,7 +670,7 @@ to move it to `refund_policy/SKILL.md`. A skill with nowhere to put a table, an
|
|
|
564
670
|
example or a script can only ever be prose, which is the half of the idea that
|
|
565
671
|
does not need a file.
|
|
566
672
|
|
|
567
|
-
Frontmatter is a deliberately small subset of YAML
|
|
673
|
+
Frontmatter is a deliberately small subset of YAML - `key: value`, plus `[a, b]`
|
|
568
674
|
flow lists for `tags` and `tools`. **Every key is optional**: `name` defaults to
|
|
569
675
|
the folder name, `description` to the first non-empty line of the body.
|
|
570
676
|
|
|
@@ -579,9 +685,9 @@ tools: [issue_refund]
|
|
|
579
685
|
|
|
580
686
|
A parcel counts as late once it passes its promised delivery date by 48 hours.
|
|
581
687
|
|
|
582
|
-
- **48h to 7 days late**
|
|
688
|
+
- **48h to 7 days late** - apologise, confirm the parcel is still moving, and
|
|
583
689
|
refund the shipping fee. Do not refund the goods.
|
|
584
|
-
- **More than 7 days late**
|
|
690
|
+
- **More than 7 days late** - treat the parcel as lost. Offer a replacement at
|
|
585
691
|
no cost, or a full refund, whichever the customer prefers.
|
|
586
692
|
|
|
587
693
|
Never give a new delivery date. Say "still in transit" instead.
|
|
@@ -594,7 +700,7 @@ skill is needed_, not as a title:
|
|
|
594
700
|
| Bad | Good |
|
|
595
701
|
| --------------------------- | ------------------------------------------------------------------------------------ |
|
|
596
702
|
| `description: Refunds` | `description: When a refund is owed, and how much.` |
|
|
597
|
-
| `description: Water policy` | `description: Escape of water from plumbing and tanks
|
|
703
|
+
| `description: Water policy` | `description: Escape of water from plumbing and tanks - and the freezing exclusion.` |
|
|
598
704
|
|
|
599
705
|
**`tools:` in frontmatter** names skill-owned tools. They are declared to the
|
|
600
706
|
provider from turn 0 (the schema never changes) but **refuse to execute** until
|
|
@@ -635,7 +741,7 @@ Rules that follow from the mount:
|
|
|
635
741
|
- The agent needs `sandbox:*` to run the script and `read_file` to open a data
|
|
636
742
|
file. Grant them, or the skill's own instructions cannot be followed (§3.5).
|
|
637
743
|
- **The interpreter must already be in the sandbox image.** A skill whose
|
|
638
|
-
script needs `pandas` is a `sandbox/Dockerfile` line that was never written
|
|
744
|
+
script needs `pandas` is a `sandbox/Dockerfile` line that was never written -
|
|
639
745
|
not a first line that runs `pip install` on every turn (§3.7).
|
|
640
746
|
- **`/skills` is read-only.** A script that produces a file must take an output
|
|
641
747
|
path and write it under `/workspace`.
|
|
@@ -644,7 +750,7 @@ Rules that follow from the mount:
|
|
|
644
750
|
directory is `/workspace`.
|
|
645
751
|
- Say what to do when the script fails, as with any other instruction (§4.2).
|
|
646
752
|
- With several `skills:` directories, one catalog is the whole of `/skills` and
|
|
647
|
-
several take `/skills/<folder>` each
|
|
753
|
+
several take `/skills/<folder>` each - check the rendered path before writing
|
|
648
754
|
it into the body.
|
|
649
755
|
- The mount is created before anything is loaded, so `allow:` limits what an
|
|
650
756
|
agent can **load**, not what it can **read**. Do not put anything in the
|
|
@@ -656,8 +762,8 @@ committed shows up as a skill with no files.
|
|
|
656
762
|
### 3.5 Tools
|
|
657
763
|
|
|
658
764
|
A tool is what an agent can _do_ rather than say. `zen run` provides three
|
|
659
|
-
groups
|
|
660
|
-
(§3.8)
|
|
765
|
+
groups - the workspace tools (§3.6), the sandbox tools (§3.7) and the web tools
|
|
766
|
+
(§3.8) - and `agents.yaml` decides which agent holds which. Nothing else reaches
|
|
661
767
|
the machine, so `tools:` is the whole permission model: an agent that does not
|
|
662
768
|
name a tool cannot use it, whatever its prompt says.
|
|
663
769
|
|
|
@@ -668,7 +774,7 @@ Two rules follow:
|
|
|
668
774
|
- **Say in the prompt when to reach for what.** A granted tool the prompt never
|
|
669
775
|
mentions is used at the model's discretion, which is not the same as never.
|
|
670
776
|
|
|
671
|
-
Skills can own tools too
|
|
777
|
+
Skills can own tools too - `tools:` in a skill's frontmatter (§3.4) names tools
|
|
672
778
|
that refuse to run until that skill is active.
|
|
673
779
|
|
|
674
780
|
### 3.6 The workspace tools (`workspace:*`)
|
|
@@ -687,12 +793,12 @@ them cannot see a file at all.
|
|
|
687
793
|
| `move_file` | Moves or renames; refuses to clobber without `overwrite` |
|
|
688
794
|
| `delete_file` | Deletes; a directory needs `recursive` |
|
|
689
795
|
|
|
690
|
-
Every path is resolved through one containment gate
|
|
691
|
-
checked
|
|
796
|
+
Every path is resolved through one containment gate - symlinks followed, then
|
|
797
|
+
checked - so nothing outside the workspace root is reachable. Reads are capped
|
|
692
798
|
and listings bounded, so no single call can flood the context.
|
|
693
799
|
|
|
694
|
-
Paths are written relative to the root, and `/workspace/...`
|
|
695
|
-
directory has inside the sandbox (§3.7)
|
|
800
|
+
Paths are written relative to the root, and `/workspace/...` - the name the same
|
|
801
|
+
directory has inside the sandbox (§3.7) - is accepted as well, so a path copied
|
|
696
802
|
out of a command's output does not have to be translated first. When a mount is
|
|
697
803
|
configured the tools also _report_ that name, so the two toolsets speak one
|
|
698
804
|
vocabulary and a path can be passed from either to the other unchanged; with no
|
|
@@ -720,7 +826,7 @@ agents:
|
|
|
720
826
|
|
|
721
827
|
Selectors apply in the order written, so a `-` line reads as an exception to the
|
|
722
828
|
line above it. Quote a lone `'*'`: unquoted, YAML reads it as an alias and
|
|
723
|
-
refuses the file. `workspace:*` needs no quoting. There is no name globbing
|
|
829
|
+
refuses the file. `workspace:*` needs no quoting. There is no name globbing -
|
|
724
830
|
`read_*` is an unknown tool, because a selector should track a declared set, not
|
|
725
831
|
a naming habit.
|
|
726
832
|
|
|
@@ -730,7 +836,7 @@ asks for: the deployment overriding the repository, as everywhere else.
|
|
|
730
836
|
**Prompting for them.** Three lines earn their place in any prompt that grants
|
|
731
837
|
this group:
|
|
732
838
|
|
|
733
|
-
- Read before editing
|
|
839
|
+
- Read before editing - `apply_patch` matches on exact text, so a patch built
|
|
734
840
|
from memory fails.
|
|
735
841
|
- Prefer `apply_patch` to `write_file` for an existing file. Rewriting a file to
|
|
736
842
|
change one line costs the whole file in output tokens and loses everything the
|
|
@@ -751,7 +857,7 @@ build, test, install and inspect rather than only read and write.
|
|
|
751
857
|
| `read_command_output` | A window of a job's output from `start_line`, plus whether it still runs |
|
|
752
858
|
| `stop_command` | Signals a job's process group |
|
|
753
859
|
|
|
754
|
-
The container is the boundary. Nothing inspects the command
|
|
860
|
+
The container is the boundary. Nothing inspects the command - there is no
|
|
755
861
|
allow-list of binaries and no pattern matching on what the model wrote, because
|
|
756
862
|
both are trivially defeated and neither survives a shell. What holds is that
|
|
757
863
|
only the workspace and the session's `/home/agent` are mounted, the container
|
|
@@ -759,12 +865,12 @@ is removed at the end of the session, and the command is never a shell argument
|
|
|
759
865
|
on the host: it travels on stdin to `/bin/sh` inside.
|
|
760
866
|
|
|
761
867
|
**Configuring it.** A top-level `sandbox:` block in `agents.yaml` describes the
|
|
762
|
-
container. Every field has a default, so the block is optional
|
|
868
|
+
container. Every field has a default, so the block is optional - write only the
|
|
763
869
|
lines that differ:
|
|
764
870
|
|
|
765
871
|
```yaml
|
|
766
872
|
sandbox:
|
|
767
|
-
persist: true # recommended
|
|
873
|
+
persist: true # recommended - see below
|
|
768
874
|
image: docker.io/library/python:3.14-slim-bookworm # the default
|
|
769
875
|
cpus: 4 # fractional cores
|
|
770
876
|
memory: 4096 # MiB
|
|
@@ -773,25 +879,36 @@ sandbox:
|
|
|
773
879
|
env: [HTTPS_PROXY, NO_PROXY] # host variables to forward, by NAME
|
|
774
880
|
```
|
|
775
881
|
|
|
776
|
-
| Field | Default | Meaning
|
|
777
|
-
| --------- | --------------------------------------------- |
|
|
778
|
-
| `image` | `docker.io/library/python:3.14-slim-bookworm` | The base image commands run in
|
|
779
|
-
| `build` | none | A Dockerfile to build instead
|
|
780
|
-
| `cpus` | the host's | Fractional cores
|
|
781
|
-
| `memory` | the host's | MiB
|
|
782
|
-
| `network` | `bridge` | `bridge` / `none` / `host`
|
|
783
|
-
| `workdir` | `/workspace` | Mount point and default cwd
|
|
784
|
-
| `timeout` | `120` | Seconds per command
|
|
785
|
-
| `user` | the image's | uid, name, or `uid:gid`
|
|
786
|
-
| `persist` | `false`
|
|
787
|
-
| `env` | none | Host variables to forward, **names**
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
882
|
+
| Field | Default | Meaning |
|
|
883
|
+
| --------- | --------------------------------------------- | --------------------------------------------- |
|
|
884
|
+
| `image` | `docker.io/library/python:3.14-slim-bookworm` | The base image commands run in |
|
|
885
|
+
| `build` | none | A Dockerfile to build instead |
|
|
886
|
+
| `cpus` | the host's | Fractional cores |
|
|
887
|
+
| `memory` | the host's | MiB |
|
|
888
|
+
| `network` | `bridge` | `bridge` / `none` / `host` |
|
|
889
|
+
| `workdir` | `/workspace` | Mount point and default cwd |
|
|
890
|
+
| `timeout` | `120` | Seconds per command |
|
|
891
|
+
| `user` | the image's | uid, name, or `uid:gid` |
|
|
892
|
+
| `persist` | `false` - **set it to `true`** | Keep the container between runs of a session |
|
|
893
|
+
| `env` | none | Host variables to forward, **names** |
|
|
894
|
+
| `keys` | `true` | Whether model credentials reach the container |
|
|
895
|
+
|
|
896
|
+
`env:` takes **names, never values** - a value here would be a secret in the
|
|
897
|
+
repository - and anything credential-shaped (`KEY`, `TOKEN`, `SECRET`,
|
|
791
898
|
`PASSWORD`, `CREDENTIAL`) is refused at load.
|
|
792
899
|
|
|
900
|
+
**Model credentials are already inside the container** (`keys:`, default
|
|
901
|
+
`true`). Every provider key the run holds is forwarded by name, and a Vertex
|
|
902
|
+
service-account file is bind-mounted read-only at `/run/zenera/keys` with
|
|
903
|
+
`GOOGLE_APPLICATION_CREDENTIALS` rewritten to point there - wherever it sits on
|
|
904
|
+
the host, keyring included. So a `zen` command that embeds (`zen rag … search`)
|
|
905
|
+
works in the sandbox: do not assume otherwise, do not route around it, and
|
|
906
|
+
never copy a key file into the project to "reach" the container. Only
|
|
907
|
+
`keys: false` / `zen run --no-keys` withholds them, and `network: none` is what
|
|
908
|
+
stops the call going out.
|
|
909
|
+
|
|
793
910
|
**`build:`, when no published image fits.** A project that needs two runtimes,
|
|
794
|
-
or a pinned toolchain, names a Dockerfile instead of an image
|
|
911
|
+
or a pinned toolchain, names a Dockerfile instead of an image - `zen init`
|
|
795
912
|
writes one at `sandbox/Dockerfile` with Python and Node in it:
|
|
796
913
|
|
|
797
914
|
```yaml
|
|
@@ -802,7 +919,7 @@ sandbox:
|
|
|
802
919
|
# context: . # what the build may COPY from; the Dockerfile's folder by default
|
|
803
920
|
```
|
|
804
921
|
|
|
805
|
-
`image:` and `build:` cannot both be set
|
|
922
|
+
`image:` and `build:` cannot both be set - a Dockerfile names its own base in
|
|
806
923
|
its `FROM` line. The tag is a hash of the Dockerfile and its context, so
|
|
807
924
|
editing it produces a new image and, like any other change here, a new
|
|
808
925
|
container. `zen check` builds it and runs a command in it, so a Dockerfile that
|
|
@@ -834,12 +951,12 @@ agents:
|
|
|
834
951
|
|
|
835
952
|
**Write `persist: true` unless you have a reason not to.** By default the
|
|
836
953
|
container is _removed_ when the session closes, and only two paths survive it:
|
|
837
|
-
`/workspace`, and `/home/agent`
|
|
954
|
+
`/workspace`, and `/home/agent` - which is `$HOME` inside, backed by the session
|
|
838
955
|
directory. That covers `pip install --user`, `npm config` and `~/.cache`, but it
|
|
839
956
|
does **not** cover the ordinary thing an agent actually does: `pip install X` or
|
|
840
957
|
`apt-get install X` as root writes to the container's system paths, and those
|
|
841
958
|
are gone on the next `zen run`. The agent then reinstalls, silently, every
|
|
842
|
-
single time
|
|
959
|
+
single time - and usually does not realise it has, because the previous run's
|
|
843
960
|
transcript says it succeeded.
|
|
844
961
|
|
|
845
962
|
```yaml
|
|
@@ -857,7 +974,7 @@ Changing any field renames the container, so bumping the image gets a fresh one
|
|
|
857
974
|
rather than an old one quietly persisting with the wrong contents. That is also
|
|
858
975
|
the one sharp edge of `persist: true`: a config change abandons the old
|
|
859
976
|
container with whatever was installed in it, so a long-lived setup still belongs
|
|
860
|
-
in `image:`
|
|
977
|
+
in `image:` - or in `sandbox/Dockerfile` - rather than in an accumulated rootfs.
|
|
861
978
|
|
|
862
979
|
Granting the group is what makes the project need Podman: `zen run` checks the
|
|
863
980
|
engine before the first turn and exits `5` with an install command if it is
|
|
@@ -867,15 +984,15 @@ reference: `docs/agents-yaml.md`.
|
|
|
867
984
|
**Prompting for them.** Two lines earn their place:
|
|
868
985
|
|
|
869
986
|
- The workspace is at `/workspace` and is the same directory the file tools
|
|
870
|
-
see
|
|
987
|
+
see - an edit made with `apply_patch` is what a command will compile, and a
|
|
871
988
|
path from either side works on both.
|
|
872
|
-
- Anything that does not return, returns
|
|
989
|
+
- Anything that does not return, returns - use `run_command_background` for a
|
|
873
990
|
server, not `run_command` with a large timeout.
|
|
874
991
|
|
|
875
992
|
### 3.8 The web tools (`exa:*`)
|
|
876
993
|
|
|
877
994
|
The third group `zen run` builds. These reach the live web through
|
|
878
|
-
[Exa](https://exa.ai)
|
|
995
|
+
[Exa](https://exa.ai) - a search index built for models rather than for people,
|
|
879
996
|
so a query is a sentence describing what is wanted, not a bag of keywords.
|
|
880
997
|
|
|
881
998
|
| Tool | What it does |
|
|
@@ -886,7 +1003,7 @@ so a query is a sentence describing what is wanted, not a bag of keywords.
|
|
|
886
1003
|
|
|
887
1004
|
The three are meant to be used in that order: **search to find, read to quote.**
|
|
888
1005
|
An excerpt is enough to judge which source to trust and never enough to cite
|
|
889
|
-
from
|
|
1006
|
+
from - `web_search` returns the sentences that made a page match, not the page.
|
|
890
1007
|
`web_answer` runs a search _and_ a model on the other side, so it is the slowest
|
|
891
1008
|
and dearest of the three; it earns its cost when the answer is a fact spread
|
|
892
1009
|
over several pages, and wastes it when a specific document is wanted.
|
|
@@ -903,7 +1020,7 @@ agents:
|
|
|
903
1020
|
|
|
904
1021
|
**The key.** All three read `$EXA_API_KEY` **when they are called**, not when the
|
|
905
1022
|
project loads. So a project naming `exa:*` still loads on a machine that has no
|
|
906
|
-
key
|
|
1023
|
+
key - the tools simply refuse, on the turn that tried, saying which variable is
|
|
907
1024
|
missing. Get a key from <https://dashboard.exa.ai/api-keys> and hold it in
|
|
908
1025
|
either place:
|
|
909
1026
|
|
|
@@ -922,14 +1039,14 @@ project is still valid, it just cannot search yet.
|
|
|
922
1039
|
| Argument | On | Why it exists |
|
|
923
1040
|
| ------------------------------------------ | ------------ | ------------------------------------------------------------------------------- |
|
|
924
1041
|
| `num_results` | `web_search` | 8 by default, 25 at most |
|
|
925
|
-
| `include_domains` / `exclude_domains` | `web_search` | The replacement for `site:`
|
|
1042
|
+
| `include_domains` / `exclude_domains` | `web_search` | The replacement for `site:` - operators in the query text do not work here |
|
|
926
1043
|
| `start_published_date` / `end_published_…` | `web_search` | ISO 8601. The only reliable way to exclude a stale answer |
|
|
927
1044
|
| `category` | `web_search` | `company`, `publication`, `news`, `personal site`, `financial report`, `people` |
|
|
928
1045
|
| `max_characters` | `web_read` | 4 000 by default, 10 000 at most; `truncated` says when a page was cut |
|
|
929
1046
|
| `max_age_hours` | `web_read` | `0` forces a live crawl. Omit it unless the page changes by the hour |
|
|
930
1047
|
|
|
931
|
-
Every reply is bounded
|
|
932
|
-
of text
|
|
1048
|
+
Every reply is bounded - pages are cut at the cap and the whole call at 128 KiB
|
|
1049
|
+
of text - so one call cannot flood the context. Each carries `cost_usd`, which
|
|
933
1050
|
is what the vendor charged for that call.
|
|
934
1051
|
|
|
935
1052
|
A failure is **reported, not raised**: a refused key, an exhausted balance, a
|
|
@@ -940,7 +1057,7 @@ did load, so one bad link does not lose the rest.
|
|
|
940
1057
|
**Prompting for them.** Three lines earn their place in any prompt granting this
|
|
941
1058
|
group:
|
|
942
1059
|
|
|
943
|
-
- Search with a sentence, not keywords
|
|
1060
|
+
- Search with a sentence, not keywords - the query is read by a model.
|
|
944
1061
|
- Never quote an excerpt. `web_search` says which page to open; `web_read` says
|
|
945
1062
|
what it contains.
|
|
946
1063
|
- Say when the web is allowed to override what the model already believes, and
|
|
@@ -965,7 +1082,7 @@ zen run --read-only withhold every tool that can write
|
|
|
965
1082
|
|
|
966
1083
|
The project is read **once, up front**: a missing prompt, an unknown tool name,
|
|
967
1084
|
a handoff to nobody all fail before the first call, with the offending key
|
|
968
|
-
named. Flags always win over the file
|
|
1085
|
+
named. Flags always win over the file - the repository states intent, the
|
|
969
1086
|
invocation overrides it.
|
|
970
1087
|
|
|
971
1088
|
A session owns a workspace, a trajectory, memory and whatever the agent wrote,
|
|
@@ -980,17 +1097,17 @@ GOOGLE_APPLICATION_CREDENTIALS=./.keys/vertex.json
|
|
|
980
1097
|
EXA_API_KEY=...
|
|
981
1098
|
```
|
|
982
1099
|
|
|
983
|
-
Never commit. Never inline a key into `agents.yaml`
|
|
1100
|
+
Never commit. Never inline a key into `agents.yaml` - use `${VAR}`. Never print a
|
|
984
1101
|
key in a log line, a test fixture, or a chat message.
|
|
985
1102
|
|
|
986
1103
|
### 3.11 `assets/`
|
|
987
1104
|
|
|
988
|
-
`assets/` next to `agents.yaml`
|
|
1105
|
+
`assets/` next to `agents.yaml` - or `assets: <path>` - is reference material
|
|
989
1106
|
every agent can read and none can write. It is mounted at `/assets`: the file
|
|
990
1107
|
tools read, list and search it, `run_command` sees it bind-mounted read-only,
|
|
991
1108
|
and every tool that would change it refuses.
|
|
992
1109
|
|
|
993
|
-
Put a handbook, a specification, a schema or a style guide there
|
|
1110
|
+
Put a handbook, a specification, a schema or a style guide there - what agents
|
|
994
1111
|
consult while working, as opposed to the workspace, which is the work. Do not
|
|
995
1112
|
point `assets:` at the project root: that hands every agent the `sessions/`
|
|
996
1113
|
directory, every transcript of every run, including the one reading it.
|
|
@@ -1006,13 +1123,14 @@ is a different project, not a different key.
|
|
|
1006
1123
|
### 4.1 Composition order
|
|
1007
1124
|
|
|
1008
1125
|
```
|
|
1009
|
-
1.
|
|
1010
|
-
2. agents
|
|
1011
|
-
3.
|
|
1012
|
-
4.
|
|
1126
|
+
1. agents/instructions.md (shared, once, all agents)
|
|
1127
|
+
2. agents/*-instructions.md (shared too, filename order)
|
|
1128
|
+
3. agents/prompts/<n>.md (this agent)
|
|
1129
|
+
4. skill index (rendered by the runtime - do not hand-write it)
|
|
1130
|
+
5. preloaded skills (activated before the first call)
|
|
1013
1131
|
```
|
|
1014
1132
|
|
|
1015
|
-
Never duplicate
|
|
1133
|
+
Never duplicate house-rules content into an agent prompt; never hand-render
|
|
1016
1134
|
a list of skills into a prompt (the runtime does it, and a hand-written one goes
|
|
1017
1135
|
stale silently).
|
|
1018
1136
|
|
|
@@ -1022,7 +1140,7 @@ stale silently).
|
|
|
1022
1140
|
reference." Not "The agent should be able to look up policies."
|
|
1023
1141
|
2. **Name things literally.** Tools in backticks by their exact name; agents by
|
|
1024
1142
|
their exact `name:`. The model matches strings.
|
|
1025
|
-
3. **State the boundary and the reason.** "Do not quote coverage rules
|
|
1143
|
+
3. **State the boundary and the reason.** "Do not quote coverage rules - you do
|
|
1026
1144
|
not have them." A prohibition without a reason gets rationalised away.
|
|
1027
1145
|
4. **Prefer prohibitions that are specific.** "Never say 'approved'" beats "be
|
|
1028
1146
|
careful about commitments".
|
|
@@ -1045,7 +1163,7 @@ stale silently).
|
|
|
1045
1163
|
|
|
1046
1164
|
Write like a competent colleague briefing a new hire on their first day: short
|
|
1047
1165
|
sentences, concrete nouns, the reason behind each rule. The model mirrors the
|
|
1048
|
-
register it is given
|
|
1166
|
+
register it is given - a prompt written in bureaucratic hedging produces
|
|
1049
1167
|
bureaucratic hedging.
|
|
1050
1168
|
|
|
1051
1169
|
### 4.4 Debugging a prompt
|
|
@@ -1054,8 +1172,8 @@ When behaviour is wrong, in this order:
|
|
|
1054
1172
|
|
|
1055
1173
|
1. **Read the actual assembled prompt**, not the file. Use the inspect report.
|
|
1056
1174
|
2. Is the instruction _present_? (Missed skill load, wrong agent, compaction.)
|
|
1057
|
-
3. Is it _contradicted_ by
|
|
1058
|
-
win over distant ones; later text usually wins over earlier.
|
|
1175
|
+
3. Is it _contradicted_ by a house-rules file or a skill? Adjacent
|
|
1176
|
+
contradictions win over distant ones; later text usually wins over earlier.
|
|
1059
1177
|
4. Is it _specific enough to be checkable_? Rewrite as a testable assertion.
|
|
1060
1178
|
5. Only then consider a stronger model.
|
|
1061
1179
|
|
|
@@ -1070,15 +1188,15 @@ it forever.
|
|
|
1070
1188
|
|
|
1071
1189
|
The decision for every piece of knowledge:
|
|
1072
1190
|
|
|
1073
|
-
| Where
|
|
1074
|
-
|
|
|
1075
|
-
| `
|
|
1076
|
-
| agent prompt
|
|
1077
|
-
| **preloaded skill**
|
|
1078
|
-
| **indexed skill**
|
|
1079
|
-
| **searched skill**
|
|
1080
|
-
| **tool**
|
|
1081
|
-
| **memory**
|
|
1191
|
+
| Where | Cost | Use when |
|
|
1192
|
+
| ------------------------- | ----------------------- | ----------------------------------------------------- |
|
|
1193
|
+
| `agents/*instructions.md` | every call, every agent | true always, for everyone |
|
|
1194
|
+
| agent prompt | every call, one agent | true always, for this job |
|
|
1195
|
+
| **preloaded skill** | every call, one agent | always needed, but versioned/shared separately |
|
|
1196
|
+
| **indexed skill** | one line until loaded | needed _sometimes_, model can tell when from one line |
|
|
1197
|
+
| **searched skill** | nothing until searched | catalog too large to index (>~30 entries) |
|
|
1198
|
+
| **tool** | schema only | needs live data, exact arithmetic, or a side effect |
|
|
1199
|
+
| **memory** | recall block when hit | learned across runs, not authored |
|
|
1082
1200
|
|
|
1083
1201
|
### 5.2 Skill bindings
|
|
1084
1202
|
|
|
@@ -1093,28 +1211,28 @@ agents:
|
|
|
1093
1211
|
maxIndexEntries: 20 # cap the rendered index
|
|
1094
1212
|
```
|
|
1095
1213
|
|
|
1096
|
-
- **`index`**
|
|
1214
|
+
- **`index`** - names and descriptions rendered into the system prompt. The
|
|
1097
1215
|
default and right answer for most catalogs.
|
|
1098
|
-
- **`search`**
|
|
1216
|
+
- **`search`** - no index, only a `skill_search` tool. For catalogs too large to
|
|
1099
1217
|
render.
|
|
1100
|
-
- **`none`**
|
|
1101
|
-
- **`preload`**
|
|
1218
|
+
- **`none`** - preloads only.
|
|
1219
|
+
- **`preload`** - for content there is no case for the model to decline: house
|
|
1102
1220
|
tone, a formatting contract. Making it choose is a wasted round trip.
|
|
1103
1221
|
Preloaded skills are filtered _out_ of the rendered index, and their activation
|
|
1104
1222
|
lands at the head of the transcript, inside the cached prefix.
|
|
1105
|
-
- **`allow`**
|
|
1223
|
+
- **`allow`** - how one catalog serves several agents without each seeing all of
|
|
1106
1224
|
it. A `preload` entry must also be in `allow`.
|
|
1107
1225
|
|
|
1108
1226
|
### 5.3 Memory
|
|
1109
1227
|
|
|
1110
1228
|
Memory is the read-write twin of skills: written by the run, not authored. It is
|
|
1111
|
-
a **graph**
|
|
1112
|
-
another
|
|
1229
|
+
a **graph** - a node is one thing worth keeping, an edge says how one led to
|
|
1230
|
+
another - so recall returns the connected piece rather than a ranked list, and a
|
|
1113
1231
|
remembered script arrives with the request that asked for it.
|
|
1114
1232
|
|
|
1115
1233
|
It is configured in `agents.yaml`: a top-level `memory:` block says where the
|
|
1116
1234
|
graph lives and what vectorises it, and `agents[].memory` says what each agent
|
|
1117
|
-
may do with it. It lives with the **project**, not the session
|
|
1235
|
+
may do with it. It lives with the **project**, not the session - a memory that
|
|
1118
1236
|
died with the session would be a cache. Recall is automatic on turns that follow
|
|
1119
1237
|
new user input, which is what makes memory work without the model remembering to
|
|
1120
1238
|
look.
|
|
@@ -1131,25 +1249,58 @@ Every node carries an **audience**, and an agent's `sees` is the set it reads:
|
|
|
1131
1249
|
one graph, masked per agent, never one graph each. The four `memory_*` tools are
|
|
1132
1250
|
derived from `access` and are never listed in `tools:`.
|
|
1133
1251
|
|
|
1252
|
+
**Memory comes on.** `zen init` writes the block above, `memory: true` on the
|
|
1253
|
+
default agent, and the half nobody would guess was part of it:
|
|
1254
|
+
`agents/memory-instructions.md`, a copy of the `zen-memory` skill's
|
|
1255
|
+
`references/memory-instructions.md`, which the project already holds. Adding
|
|
1256
|
+
memory to a project that has none is both of those edits, and a copy that was
|
|
1257
|
+
lost or went stale is one command:
|
|
1258
|
+
|
|
1259
|
+
```sh
|
|
1260
|
+
.github/skills/zen-memory/scripts/check-instructions.sh fix
|
|
1261
|
+
```
|
|
1262
|
+
|
|
1263
|
+
It is what tells the agent that the store is reached only through the tools, that
|
|
1264
|
+
`memory_search` clips a node's text and `memory_load` reads it whole, that a
|
|
1265
|
+
remembered file's path comes from `memory_load` and not from listing `/memory`,
|
|
1266
|
+
what a node must carry to be worth having, and that a correction is a
|
|
1267
|
+
`SUPERSEDES` edge rather than an edit. It is its own file rather than a section of
|
|
1268
|
+
`agents/instructions.md` because it arrives with the `memory:` block and leaves
|
|
1269
|
+
with it.
|
|
1270
|
+
|
|
1271
|
+
**Leave that file as a pure copy.** Anything this project decides for itself -
|
|
1272
|
+
which audience holds what, what must never be written down, what an agent is
|
|
1273
|
+
expected to commit at the end of a job - goes in
|
|
1274
|
+
`agents/memory-policy-instructions.md`. Topic files are read in filename order,
|
|
1275
|
+
so the policy lands directly after the rules it qualifies, and the copy stays
|
|
1276
|
+
re-runnable: the reference is rewritten by every `zen init` and `zen open`, and
|
|
1277
|
+
keeping up with it is then the same one-line command rather than a hand-patch.
|
|
1278
|
+
|
|
1279
|
+
`zen check` validates the `memory:` block and says nothing about the rules'
|
|
1280
|
+
absence, so this is checked every time, by
|
|
1281
|
+
`.github/skills/zen-memory/scripts/check-instructions.sh` and never by reading
|
|
1282
|
+
the two files against each other: they drift by trailing whitespace inside a
|
|
1283
|
+
table, and the script is what can see that.
|
|
1284
|
+
|
|
1134
1285
|
Do not treat memory as a database. It is for things learned that should
|
|
1135
|
-
persist. Anything authoritative
|
|
1286
|
+
persist. Anything authoritative - a rate, a policy clause, a procedure - belongs
|
|
1136
1287
|
in a skill, where it is versioned and reviewable.
|
|
1137
1288
|
|
|
1138
|
-
The full treatment
|
|
1289
|
+
The full treatment - kinds and relations, how ranking and stitching spend their
|
|
1139
1290
|
budget, remembered files under `/memory`, audience design, and how to put memory
|
|
1140
|
-
in front of a `zen rag` index
|
|
1291
|
+
in front of a `zen rag` index - is in the `zen-memory` skill.
|
|
1141
1292
|
|
|
1142
1293
|
### 5.4 Cache discipline
|
|
1143
1294
|
|
|
1144
1295
|
Rules that follow directly from "stable prefix = cache hit":
|
|
1145
1296
|
|
|
1146
|
-
- Do not reorder `agents.yaml` for cosmetic reasons
|
|
1147
|
-
- Do not put timestamps, run ids, or "today is …" in
|
|
1297
|
+
- Do not reorder `agents.yaml` for cosmetic reasons - tool order is prompt order.
|
|
1298
|
+
- Do not put timestamps, run ids, or "today is …" in a house-rules file or a
|
|
1148
1299
|
prompt. A changing prefix is a permanent cache miss. Put volatile facts in a
|
|
1149
1300
|
tool result.
|
|
1150
1301
|
- Prefer `preload` over an instruction telling the model to load a skill first.
|
|
1151
1302
|
- Keep the volatile half of an instruction in the agent prompt and the stable
|
|
1152
|
-
half in
|
|
1303
|
+
half in the house rules, not the reverse.
|
|
1153
1304
|
|
|
1154
1305
|
---
|
|
1155
1306
|
|
|
@@ -1169,10 +1320,10 @@ Split into a second agent when at least one is true:
|
|
|
1169
1320
|
|
|
1170
1321
|
Do **not** split because:
|
|
1171
1322
|
|
|
1172
|
-
- The prompt got long
|
|
1173
|
-
- It feels tidier
|
|
1323
|
+
- The prompt got long - split it into skills instead.
|
|
1324
|
+
- It feels tidier - every handoff costs a full re-render of the system prompt and
|
|
1174
1325
|
a fresh cache prefix.
|
|
1175
|
-
- You want "a planner and an executor" with no distinct tools or models
|
|
1326
|
+
- You want "a planner and an executor" with no distinct tools or models - that is
|
|
1176
1327
|
one agent with a numbered procedure.
|
|
1177
1328
|
|
|
1178
1329
|
**Start with one agent. Add the second when a specific case forces it.**
|
|
@@ -1181,7 +1332,7 @@ Do **not** split because:
|
|
|
1181
1332
|
|
|
1182
1333
|
**Router + specialists.** A cheap, fast agent whose only job is classification
|
|
1183
1334
|
and handoff. Its prompt is short, it holds few tools, and it must be forbidden
|
|
1184
|
-
from answering. Every specialist hands back to it
|
|
1335
|
+
from answering. Every specialist hands back to it - a handoff does not return on
|
|
1185
1336
|
its own, so without that edge the first routed question is the last one routed.
|
|
1186
1337
|
|
|
1187
1338
|
```yaml
|
|
@@ -1193,11 +1344,11 @@ agents:
|
|
|
1193
1344
|
```
|
|
1194
1345
|
|
|
1195
1346
|
**Pipeline.** Fixed stages, each handing to the next; only the last answers the
|
|
1196
|
-
user. Encode the order in `handoffs:` so a stage cannot skip ahead
|
|
1347
|
+
user. Encode the order in `handoffs:` so a stage cannot skip ahead - and close
|
|
1197
1348
|
the loop, by handing back from the last stage to the first.
|
|
1198
1349
|
|
|
1199
|
-
**Fan-out / join.** For independent parallel work
|
|
1200
|
-
lenses, six candidate suppliers
|
|
1350
|
+
**Fan-out / join.** For independent parallel work - ten regions, four review
|
|
1351
|
+
lenses, six candidate suppliers - declare `fork:` on the agent that owns the
|
|
1201
1352
|
work and let the model split it. See §6.4.
|
|
1202
1353
|
|
|
1203
1354
|
**Single agent + rich catalog.** One agent, `discovery: index`, twenty skills.
|
|
@@ -1208,11 +1359,11 @@ multi-agent instinct suggests.
|
|
|
1208
1359
|
|
|
1209
1360
|
- `description:` on the target agent is what the model reads when deciding.
|
|
1210
1361
|
Write it as a routing condition: _"Applies the written peril policies to a
|
|
1211
|
-
claim and explains the outcome."_
|
|
1362
|
+
claim and explains the outcome."_ - not _"The adjuster agent."_
|
|
1212
1363
|
- Handoffs are bare name strings; there is no per-edge configuration.
|
|
1213
1364
|
- Self-handoff is a load error.
|
|
1214
1365
|
- **Every handoff must be returnable.** For every `A → B` there must be a path
|
|
1215
|
-
from `B` back to `A`. Indirect counts
|
|
1366
|
+
from `B` back to `A`. Indirect counts - `B → C → A` is a way home - so the
|
|
1216
1367
|
rule is that every handoff edge lies on a cycle. `zen check` warns
|
|
1217
1368
|
(`handoff.one-way`) on any edge that does not.
|
|
1218
1369
|
- Handoff collapses history by policy: the receiving agent sees a selection, not
|
|
@@ -1221,7 +1372,7 @@ multi-agent instinct suggests.
|
|
|
1221
1372
|
|
|
1222
1373
|
**Why the return edge is not optional.** A handoff is a one-way door: control
|
|
1223
1374
|
moves and stays moved. Nothing hands it back, so an agent reached by a dead-end
|
|
1224
|
-
edge owns the conversation for the rest of the session
|
|
1375
|
+
edge owns the conversation for the rest of the session - the next question,
|
|
1225
1376
|
whatever it is about, is answered by the specialist the router sent the user to.
|
|
1226
1377
|
The drawn architecture holds for one turn and then quietly stops being the
|
|
1227
1378
|
architecture.
|
|
@@ -1234,10 +1385,11 @@ interchangeable:
|
|
|
1234
1385
|
- The other agent should **own the conversation from here** → `handoffs:`, and
|
|
1235
1386
|
something on the far side has to lead back.
|
|
1236
1387
|
- This agent needs **an answer and then carries on** → `fork:`. Never a handoff:
|
|
1237
|
-
a handoff spends the conversation to get the answer.
|
|
1388
|
+
a handoff spends the conversation to get the answer. One branch is enough -
|
|
1389
|
+
a fork of one is exactly "ask the specialist and continue".
|
|
1238
1390
|
|
|
1239
1391
|
But the return is **condensed**: `A → fork → B` rejoins as one tool result
|
|
1240
|
-
holding B's answer, so A never sees the steps B took
|
|
1392
|
+
holding B's answer, so A never sees the steps B took - no tool calls, no files
|
|
1241
1393
|
read, no intermediate reasoning. That is what makes fanning out cheap, and it is
|
|
1242
1394
|
what to design around: whatever A will need must be _in_ the answer, so say so in
|
|
1243
1395
|
B's prompt. Work whose value is the trace rather than the conclusion does not
|
|
@@ -1262,47 +1414,51 @@ agents:
|
|
|
1262
1414
|
| Field | Default | Meaning |
|
|
1263
1415
|
| ------------- | -------------------- | ------------------------------------- |
|
|
1264
1416
|
| `agents` | every declared agent | Which agents a branch may run |
|
|
1265
|
-
| `maxBranches` | unlimited | Cap on branches per call; minimum `
|
|
1417
|
+
| `maxBranches` | unlimited | Cap on branches per call; minimum `1` |
|
|
1266
1418
|
|
|
1267
1419
|
The **model** decides the split: it names N branches, each with self-contained
|
|
1268
1420
|
instructions. They run truly concurrently and rejoin as **one tool call and one
|
|
1269
|
-
tool result** in the parent's history
|
|
1421
|
+
tool result** in the parent's history - so N branches cost the parent
|
|
1270
1422
|
O(N × summary), not O(N × full history).
|
|
1271
1423
|
|
|
1272
1424
|
Rules worth knowing before you write the key:
|
|
1273
1425
|
|
|
1274
|
-
- `agents:` **may include the forking agent itself**
|
|
1426
|
+
- `agents:` **may include the forking agent itself** - unlike `handoffs:`, that
|
|
1275
1427
|
is not an error, and one role fanned out over ten items is the common shape.
|
|
1276
1428
|
The list reaches the model as an `enum`, so a name outside it cannot even be
|
|
1277
1429
|
decoded.
|
|
1278
|
-
- A fork
|
|
1279
|
-
|
|
1430
|
+
- A fork of **one** branch is delegation, and it is valid: one assignment run
|
|
1431
|
+
elsewhere, its conclusion returned, its transcript left out of this
|
|
1432
|
+
conversation. Use it when another agent suits the job better, or when the job
|
|
1433
|
+
would fill the caller's context with material it has no use for afterwards.
|
|
1280
1434
|
- **Branches cannot talk to each other.** If branch B needs branch A's answer,
|
|
1281
|
-
it is a sequence, not a fork
|
|
1435
|
+
it is a sequence, not a fork - keep it in one conversation.
|
|
1282
1436
|
- Nesting is capped by the run's `maxForkDepth` (2 by default), so a branch may
|
|
1283
1437
|
fork again but not without bound.
|
|
1284
1438
|
- Fork vs handoff: a handoff is _one_ conversation changing owner; a fork is the
|
|
1285
1439
|
_same_ question asked N times at once and merged. **A fork returns; a handoff
|
|
1286
|
-
does not**
|
|
1440
|
+
does not** - which is why a handoff needs a return path (§6.3) and a fork
|
|
1287
1441
|
needs nothing.
|
|
1288
1442
|
|
|
1289
|
-
Choose `context:` deliberately
|
|
1443
|
+
Choose `context:` deliberately - the model sets it per call, so say in the
|
|
1290
1444
|
agent's prompt which one this work wants:
|
|
1291
1445
|
|
|
1292
|
-
- `inherit`
|
|
1293
|
-
- `compact`
|
|
1446
|
+
- `inherit` - branch gets the full prefix. For work that depends on the case.
|
|
1447
|
+
- `compact` - prefix minus tool calls, tool results and recalls: _what was
|
|
1294
1448
|
decided_, not the raw noise. **The default for wide fan-outs.**
|
|
1295
|
-
- `none`
|
|
1449
|
+
- `none` - system prompt plus instructions only. Cheapest; for independent
|
|
1296
1450
|
lookups.
|
|
1297
1451
|
|
|
1298
|
-
Declaring `fork:` only makes the tool available.
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1452
|
+
Declaring `fork:` only makes the tool available. What a fork costs and what
|
|
1453
|
+
survives the join is explained to the model by the runtime, in the system prompt
|
|
1454
|
+
of every agent holding the key. What is left to you is _when_, in the terms of
|
|
1455
|
+
this domain - _"When the request covers more than one region, fork one branch
|
|
1456
|
+
per region and merge their findings"_ - or a weaker model will work through the
|
|
1457
|
+
list serially and never call it.
|
|
1302
1458
|
|
|
1303
1459
|
### 6.5 Termination
|
|
1304
1460
|
|
|
1305
|
-
A turn ends when the model replies with no tool calls
|
|
1461
|
+
A turn ends when the model replies with no tool calls - that text is the answer.
|
|
1306
1462
|
There is no turn limit, so an agent that must stop somewhere needs a prompt that
|
|
1307
1463
|
says where: what "done" looks like, and what to do when it cannot get there.
|
|
1308
1464
|
|
|
@@ -1315,7 +1471,7 @@ should contain. An agent with no stated finish keeps working the problem.
|
|
|
1315
1471
|
|
|
1316
1472
|
### 7.1 Tiers
|
|
1317
1473
|
|
|
1318
|
-
Think in three tiers, and give them **role names, not vendor names**
|
|
1474
|
+
Think in three tiers, and give them **role names, not vendor names** - so the
|
|
1319
1475
|
mapping changes in one place.
|
|
1320
1476
|
|
|
1321
1477
|
| Alias | Role | Reasoning |
|
|
@@ -1379,7 +1535,7 @@ Only the **first** colon separates, so a fine-tuned id must name its provider:
|
|
|
1379
1535
|
`openai:ft:gpt-4o:acme::a1b2`.
|
|
1380
1536
|
|
|
1381
1537
|
**Always write the prefix.** The first segment is a provider _name_, not a vendor
|
|
1382
|
-
hint
|
|
1538
|
+
hint - nothing reads `gemini-3.5-flash` and infers Google. An unprefixed id goes
|
|
1383
1539
|
to the default provider, which is `openai` unless a top-level `provider:` says
|
|
1384
1540
|
otherwise, so a bare `gemini-3.5-flash` asks OpenAI for a Google model and fails
|
|
1385
1541
|
with `OPENAI_API_KEY is not set`. The message names the provider it resolved to;
|
|
@@ -1398,26 +1554,26 @@ over one connection.
|
|
|
1398
1554
|
|
|
1399
1555
|
| Field | Applies to | Notes |
|
|
1400
1556
|
| ----------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
1401
|
-
| `reasoningEffort` | openai, openrouter | Free string on purpose
|
|
1402
|
-
| `reasoningSummary` | openai, openrouter | `auto` \| `concise` \| `detailed`. On openai **needs `api: responses`**
|
|
1557
|
+
| `reasoningEffort` | openai, openrouter | Free string on purpose - the API is the authority on validity. On openai **needs `api: responses`** - §7.6 |
|
|
1558
|
+
| `reasoningSummary` | openai, openrouter | `auto` \| `concise` \| `detailed`. On openai **needs `api: responses`** - §7.6 |
|
|
1403
1559
|
| `maxTokens` | anthropic, gemini, openrouter | Cap on **output** tokens, not context. Anthropic requires one (default 8192) |
|
|
1404
1560
|
| `thinkingBudgetTokens` | anthropic | Extended thinking budget |
|
|
1405
1561
|
| `thinkingBudget` | gemini 2.5 | Tokens: `0` off, `-1` auto |
|
|
1406
1562
|
| `thinkingLevel` | gemini 3 | `minimal` \| `low` \| `medium` \| `high` |
|
|
1407
1563
|
| `includeThoughts` | gemini | Thought summaries; default `true` |
|
|
1408
|
-
| `routing` / `fallbacks` | openrouter | Upstream provider preferences, and models to fall back to
|
|
1564
|
+
| `routing` / `fallbacks` | openrouter | Upstream provider preferences, and models to fall back to - §7.5 |
|
|
1409
1565
|
|
|
1410
1566
|
Knobs that do not apply to the chosen vendor are ignored, not rejected.
|
|
1411
|
-
`api:` exists only for the OpenAI protocol
|
|
1567
|
+
`api:` exists only for the OpenAI protocol - naming it on a Gemini or Anthropic
|
|
1412
1568
|
model is an error.
|
|
1413
1569
|
|
|
1414
1570
|
Each vendor's own SDK is used rather than its OpenAI-compatible endpoint, because
|
|
1415
1571
|
those endpoints drop exactly what this runtime is built on: thinking budgets,
|
|
1416
|
-
thought signatures, cache accounting. `openai-compatible` is the shim kind
|
|
1572
|
+
thought signatures, cache accounting. `openai-compatible` is the shim kind - vLLM,
|
|
1417
1573
|
a self-hosted gateway. `openrouter` used to be that shim with its base url
|
|
1418
1574
|
(`https://openrouter.ai/api/v1`) and key env (`OPENROUTER_API_KEY`) filled in; it
|
|
1419
1575
|
now has its own SDK, which is what makes provider routing and fallback chains
|
|
1420
|
-
available
|
|
1576
|
+
available - §7.5.
|
|
1421
1577
|
|
|
1422
1578
|
### 7.5 OpenRouter
|
|
1423
1579
|
|
|
@@ -1425,7 +1581,7 @@ A gateway: one key and one endpoint in front of several hundred models from
|
|
|
1425
1581
|
every vendor. Useful when a project wants to compare families without holding
|
|
1426
1582
|
four accounts, and when a cheap tier should be swappable by editing one id.
|
|
1427
1583
|
|
|
1428
|
-
The whole declaration is the kind
|
|
1584
|
+
The whole declaration is the kind - `baseURL` and `OPENROUTER_API_KEY` are its
|
|
1429
1585
|
defaults, and built-in kinds are usable as provider names, so a `providers:`
|
|
1430
1586
|
entry is only worth writing when it adds something:
|
|
1431
1587
|
|
|
@@ -1435,7 +1591,7 @@ agents:
|
|
|
1435
1591
|
model: openrouter:anthropic/claude-sonnet-4.5
|
|
1436
1592
|
```
|
|
1437
1593
|
|
|
1438
|
-
**Model ids** are `vendor/model` and may carry a variant suffix after a colon
|
|
1594
|
+
**Model ids** are `vendor/model` and may carry a variant suffix after a colon -
|
|
1439
1595
|
`:free`, `:nitro` (throughput-routed), `:floor` (price-routed), `:online` (web
|
|
1440
1596
|
search). Both survive the shorthand, because only the _first_ colon separates:
|
|
1441
1597
|
|
|
@@ -1458,7 +1614,7 @@ the destination model understands.
|
|
|
1458
1614
|
the upstream provider (OpenRouter's `provider` field, renamed because `provider:`
|
|
1459
1615
|
already means the connection); `fallbacks` lists other _models_ to try when none
|
|
1460
1616
|
can serve it (its `models` field). `allowFallbacks`, below, is a third thing
|
|
1461
|
-
again
|
|
1617
|
+
again - whether the gateway may look past `order`:
|
|
1462
1618
|
|
|
1463
1619
|
```yaml
|
|
1464
1620
|
models:
|
|
@@ -1478,9 +1634,9 @@ there is a load error.
|
|
|
1478
1634
|
|
|
1479
1635
|
| Key | Value |
|
|
1480
1636
|
| ------------------- | ---------------------------------------------------------------------------- |
|
|
1481
|
-
| `order` | Providers to try first
|
|
1637
|
+
| `order` | Providers to try first - a _preference_, not a restriction |
|
|
1482
1638
|
| `only` / `ignore` | Restrict serving to, or away from, these |
|
|
1483
|
-
| `allowFallbacks` | May the gateway go beyond `order`
|
|
1639
|
+
| `allowFallbacks` | May the gateway go beyond `order` - **on** unless set `false` |
|
|
1484
1640
|
| `sort` | `price`, `throughput`, `latency`, `exacto` |
|
|
1485
1641
|
| `requireParameters` | Skip a provider that would drop a parameter rather than serve it |
|
|
1486
1642
|
| `dataCollection` | `allow` \| `deny` |
|
|
@@ -1491,12 +1647,12 @@ there is a load error.
|
|
|
1491
1647
|
sits alongside `routing`, not inside it.
|
|
1492
1648
|
|
|
1493
1649
|
**A typo in `order` is invisible.** Provider names and `sort` are free strings
|
|
1494
|
-
for the same reason `reasoningEffort` is
|
|
1495
|
-
schema would
|
|
1650
|
+
for the same reason `reasoningEffort` is - the gateway's list moves faster than a
|
|
1651
|
+
schema would - so nothing local rejects them, and since `allowFallbacks` defaults
|
|
1496
1652
|
on, an unknown name is skipped and the request quietly succeeds somewhere else.
|
|
1497
1653
|
What _is_ checked is checked by the API rather than at load: a bad `sort` or
|
|
1498
1654
|
`quantizations` returns `400 provider.sort: Invalid input`. Use `only`, or
|
|
1499
|
-
`allowFallbacks: false`, when the constraint is meant to bind
|
|
1655
|
+
`allowFallbacks: false`, when the constraint is meant to bind - an unroutable
|
|
1500
1656
|
request is then a 404 instead of a silent reroute.
|
|
1501
1657
|
|
|
1502
1658
|
**Attribution** goes in `headers:`; there is no dedicated field because that one
|
|
@@ -1527,11 +1683,11 @@ curl -s https://openrouter.ai/api/v1/models | jq -r '
|
|
|
1527
1683
|
`supported_parameters` decides whether `tools`, `tool_choice` and
|
|
1528
1684
|
`reasoning_effort` are honoured. An agent with tools needs `tools` in that list.
|
|
1529
1685
|
|
|
1530
|
-
**Not modelled yet:** `transforms`, `usage.include`, and per-call cost
|
|
1686
|
+
**Not modelled yet:** `transforms`, `usage.include`, and per-call cost - the
|
|
1531
1687
|
gateway reports a price on every response, but it is not surfaced in the token
|
|
1532
1688
|
accounting. `plugins` exists in code only. Four `provider` fields the SDK accepts
|
|
1533
|
-
have no yaml spelling either
|
|
1534
|
-
`preferredMinThroughput`, `enforceDistillableText`
|
|
1689
|
+
have no yaml spelling either - `maxPrice`, `preferredMaxLatency`,
|
|
1690
|
+
`preferredMinThroughput`, `enforceDistillableText` - and `sort` takes the string
|
|
1535
1691
|
form only, not the `{ by, partition }` object. `models:` entries are strict, so
|
|
1536
1692
|
writing any of these is a load error rather than a key that is silently dropped.
|
|
1537
1693
|
|
|
@@ -1545,27 +1701,27 @@ in place. `timeoutMs` and `headers` behave normally.
|
|
|
1545
1701
|
|
|
1546
1702
|
Ask two separate questions: does the model **reason**, and does it **say what it
|
|
1547
1703
|
reasoned**. They are different knobs, and the second is off by default on every
|
|
1548
|
-
vendor except Gemini
|
|
1704
|
+
vendor except Gemini - which is why a reasoning model can burn thousands of
|
|
1549
1705
|
thinking tokens while the CLI shows no progress at all.
|
|
1550
1706
|
|
|
1551
|
-
**OpenAI**
|
|
1707
|
+
**OpenAI** - reasoning text only exists on the **responses** API, and only as a
|
|
1552
1708
|
summary. `api: responses` is not optional here, and not only for visibility:
|
|
1553
1709
|
chat completions is the default, and it **refuses `reasoningEffort` together with
|
|
1554
|
-
function tools**
|
|
1710
|
+
function tools** - `400 Function tools with reasoning_effort are not supported
|
|
1555
1711
|
for <model> in /v1/chat/completions`. So any OpenAI agent that both reasons and
|
|
1556
|
-
holds tools
|
|
1712
|
+
holds tools - which is nearly all of them - must name the api.
|
|
1557
1713
|
|
|
1558
1714
|
```yaml
|
|
1559
1715
|
models:
|
|
1560
1716
|
default:
|
|
1561
1717
|
provider: openai
|
|
1562
|
-
api: responses # required
|
|
1718
|
+
api: responses # required - chat completions refuses tools + reasoning
|
|
1563
1719
|
model: gpt-5.4-nano
|
|
1564
1720
|
reasoningEffort: medium # how hard it thinks
|
|
1565
1721
|
reasoningSummary: auto # whether you get to see it
|
|
1566
1722
|
```
|
|
1567
1723
|
|
|
1568
|
-
**Anthropic**
|
|
1724
|
+
**Anthropic** - `thinkingBudgetTokens` turns extended thinking on, and it is spent
|
|
1569
1725
|
_out of_ `maxTokens`, so raise that too or the answer has no room left after the
|
|
1570
1726
|
thinking. The runtime keeps 1024 tokens of headroom whatever you write, so a cap
|
|
1571
1727
|
below the budget is corrected rather than rejected. Read §7.7 first: this
|
|
@@ -1580,7 +1736,7 @@ models:
|
|
|
1580
1736
|
thinkingBudgetTokens: 8000
|
|
1581
1737
|
```
|
|
1582
1738
|
|
|
1583
|
-
**Gemini**
|
|
1739
|
+
**Gemini** - thought summaries are on by default (`includeThoughts: true`); what
|
|
1584
1740
|
varies is the budget. Gemini 3 takes `thinkingLevel`, Gemini 2.5 takes
|
|
1585
1741
|
`thinkingBudget` in tokens.
|
|
1586
1742
|
|
|
@@ -1597,19 +1753,19 @@ running tail above the answer, and the one-shot path prints it under `--live`.
|
|
|
1597
1753
|
The full chain is kept in the trajectory either way and is in the inspect report,
|
|
1598
1754
|
so turning summaries off costs visibility, not the audit trail.
|
|
1599
1755
|
|
|
1600
|
-
The cost is real
|
|
1756
|
+
The cost is real - a summary is extra output tokens on every call - so leave it
|
|
1601
1757
|
on where someone is watching and reach for a cheaper tier before turning effort
|
|
1602
1758
|
up (§7.8).
|
|
1603
1759
|
|
|
1604
1760
|
### 7.7 Known traps
|
|
1605
1761
|
|
|
1606
|
-
- **A model id with no provider prefix**
|
|
1762
|
+
- **A model id with no provider prefix** - `gemini-3.5-flash` resolves to the
|
|
1607
1763
|
default provider, not to Google, and the failure reads as a missing OpenAI key.
|
|
1608
|
-
Write `google:gemini-3.5-flash`
|
|
1609
|
-
- **OpenAI reasoning without `api: responses`**
|
|
1764
|
+
Write `google:gemini-3.5-flash` - §7.3.
|
|
1765
|
+
- **OpenAI reasoning without `api: responses`** - chat completions rejects
|
|
1610
1766
|
`reasoningEffort` alongside function tools with a `400`, and exposes no
|
|
1611
|
-
reasoning summary even without tools
|
|
1612
|
-
- **Anthropic + `thinkingBudgetTokens` + multi-turn tool use**
|
|
1767
|
+
reasoning summary even without tools - §7.6.
|
|
1768
|
+
- **Anthropic + `thinkingBudgetTokens` + multi-turn tool use** - thinking-block
|
|
1613
1769
|
signatures are not replayed, and the API rejects the follow-up. Leave extended
|
|
1614
1770
|
thinking off for tool-using agents.
|
|
1615
1771
|
- **`reasoningEffort: minimal`** is rejected by the `gpt-5.4-*` family; use `low`.
|
|
@@ -1617,16 +1773,16 @@ up (§7.8).
|
|
|
1617
1773
|
→ the `project_id` inside the key file named by `GOOGLE_APPLICATION_CREDENTIALS`.
|
|
1618
1774
|
gcloud user credentials and metadata-server credentials carry no project id, so
|
|
1619
1775
|
those deployments must set the variable.
|
|
1620
|
-
- **OpenRouter + a capability the route does not have**
|
|
1776
|
+
- **OpenRouter + a capability the route does not have** - a valid id and a valid
|
|
1621
1777
|
key still fail at the first request (`404 No endpoints found that support image
|
|
1622
1778
|
input`, or tools quietly unused). This is not a config error and `zen check`
|
|
1623
1779
|
cannot see it: check the catalog (§7.5). Cheap `:free` tiers are the usual
|
|
1624
|
-
offenders
|
|
1780
|
+
offenders - they are frequently text-only.
|
|
1625
1781
|
- **Swapping an OpenRouter id is not a like-for-like change.** Two models behind
|
|
1626
1782
|
one gateway differ in modalities, tool support and reasoning; re-run the case
|
|
1627
1783
|
that uses the capability, not just any case.
|
|
1628
1784
|
- **An `embeddings:` entry on an Anthropic provider** fails at load: that vendor
|
|
1629
|
-
publishes no embeddings API. Point it at another provider
|
|
1785
|
+
publishes no embeddings API. Point it at another provider - §3.1.1.
|
|
1630
1786
|
- **Changing `dimensions:` on an embedding invalidates every stored vector.**
|
|
1631
1787
|
Widths are not comparable, so anything already indexed has to be re-embedded.
|
|
1632
1788
|
|
|
@@ -1634,7 +1790,7 @@ input`, or tools quietly unused). This is not a config error and `zen check`
|
|
|
1634
1790
|
|
|
1635
1791
|
1. Start every agent on `balanced`.
|
|
1636
1792
|
2. Demote to `router` any agent whose job is classification, extraction, or a
|
|
1637
|
-
fixed handoff
|
|
1793
|
+
fixed handoff - measure, do not guess.
|
|
1638
1794
|
3. Promote to `careful` only after seeing a concrete failure that a stronger
|
|
1639
1795
|
model actually fixes.
|
|
1640
1796
|
4. Never fix an instruction-following bug with a model upgrade (§4.4).
|
|
@@ -1650,18 +1806,18 @@ There is no compiler for prose. Substitutes, in order of value:
|
|
|
1650
1806
|
1. **`zen check`.** Most structural mistakes are load errors, and this is the
|
|
1651
1807
|
fastest way to see all of them: it validates `agents.yaml`, checks that every
|
|
1652
1808
|
prompt, skill and catalog it names is on disk, that hand-offs and tool
|
|
1653
|
-
selectors resolve, and that the models have credentials
|
|
1809
|
+
selectors resolve, and that the models have credentials - without stopping at
|
|
1654
1810
|
the first problem and without calling anything. `zen check --json` if you are
|
|
1655
1811
|
parsing it.
|
|
1656
1812
|
2. **Run the case that motivated the change**, plus one that must _not_ change.
|
|
1657
|
-
3. **Read the inspect report**
|
|
1813
|
+
3. **Read the inspect report** - it shows the assembled prompt, every request and
|
|
1658
1814
|
response, tool calls, skill activations and cost. Behaviour questions are
|
|
1659
1815
|
answered there, not by re-reading the YAML.
|
|
1660
1816
|
4. **Re-run `zen check` after any rename.** It is what catches the handoff, the
|
|
1661
1817
|
skill or the prompt path that a rename silently unlinked.
|
|
1662
1818
|
5. **Watch the token accounting.** A change that doubles prefix size is a
|
|
1663
1819
|
regression even if the answer improved.
|
|
1664
|
-
6. **After changing a model id, exercise the capability it was chosen for**
|
|
1820
|
+
6. **After changing a model id, exercise the capability it was chosen for** -
|
|
1665
1821
|
send an image, force a tool call, ask for reasoning. `zen check` proves the
|
|
1666
1822
|
credential resolves, not that the route serves images or honours `tools`; on a
|
|
1667
1823
|
gateway that gap is a request-time 404 (§7.5).
|
|
@@ -1675,13 +1831,13 @@ first thing that looks like a credential problem:
|
|
|
1675
1831
|
zen models test <ref> # one real call, one verdict
|
|
1676
1832
|
```
|
|
1677
1833
|
|
|
1678
|
-
- **refused**
|
|
1679
|
-
- **blocked**
|
|
1834
|
+
- **refused** - the credential was rejected. `zen key check <provider>`.
|
|
1835
|
+
- **blocked** - the credential was _accepted_ and the account said no: an API
|
|
1680
1836
|
switched off, an empty balance, a model this key was never granted. **Do not
|
|
1681
|
-
rotate the key**
|
|
1837
|
+
rotate the key** - a new one is refused identically. The fix is printed under
|
|
1682
1838
|
the verdict; for a disabled Google API it is the exact `gcloud services
|
|
1683
1839
|
enable …` line.
|
|
1684
|
-
- **no answer**
|
|
1840
|
+
- **no answer** - the network. Try again.
|
|
1685
1841
|
|
|
1686
1842
|
Either run the printed fix, or take a model that works and put the ref it prints
|
|
1687
1843
|
into `agents.yaml`:
|
|
@@ -1701,6 +1857,16 @@ ok, `1` failed, `2` usage, `3` invalid project, `4` no usable credential.
|
|
|
1701
1857
|
|
|
1702
1858
|
## 9. Review checklist
|
|
1703
1859
|
|
|
1860
|
+
Four of these are decidable, and are one command - `zen check`, the path sweep,
|
|
1861
|
+
the memory copy, the spec-sync record, in that order:
|
|
1862
|
+
|
|
1863
|
+
```sh
|
|
1864
|
+
.github/skills/zen-review/scripts/review.sh
|
|
1865
|
+
```
|
|
1866
|
+
|
|
1867
|
+
Run it first and read what it says. Everything else below is judgement, which is
|
|
1868
|
+
why it is still a list. The `zen-review` skill has the detail.
|
|
1869
|
+
|
|
1704
1870
|
Before finishing any change here:
|
|
1705
1871
|
|
|
1706
1872
|
**Structure**
|
|
@@ -1715,26 +1881,34 @@ Before finishing any change here:
|
|
|
1715
1881
|
|
|
1716
1882
|
**Prompts**
|
|
1717
1883
|
|
|
1718
|
-
- [ ] `
|
|
1719
|
-
-
|
|
1884
|
+
- [ ] `agents/instructions.md` carries the shared architecture and nothing
|
|
1885
|
+
agent-specific
|
|
1886
|
+
- [ ] Nothing duplicated between a house-rules file and an agent prompt
|
|
1887
|
+
- [ ] Each `agents/<topic>-instructions.md` is about one capability, and is true
|
|
1888
|
+
of every agent
|
|
1720
1889
|
- [ ] Every tool and agent referenced by its exact name
|
|
1721
1890
|
- [ ] Failure paths stated for every instruction that can fail
|
|
1722
1891
|
- [ ] No facts, rates or figures embedded in a prompt
|
|
1723
1892
|
- [ ] No hedging, no meta-talk about the runtime
|
|
1893
|
+
- [ ] If any agent has `memory:`,
|
|
1894
|
+
`.github/skills/zen-memory/scripts/check-instructions.sh` exits zero -
|
|
1895
|
+
`agents/memory-instructions.md` is the reference byte for byte - and
|
|
1896
|
+
anything project-specific is in `agents/memory-policy-instructions.md` - §5.3
|
|
1724
1897
|
|
|
1725
|
-
**Paths (§2.5
|
|
1898
|
+
**Paths (§2.5 - `.github/skills/zen-review/scripts/check-paths.sh` finds the
|
|
1899
|
+
candidates; these are the judgements to make about each one)**
|
|
1726
1900
|
|
|
1727
|
-
- [ ] Every absolute path in `
|
|
1901
|
+
- [ ] Every absolute path in `agents/*instructions.md`, `agents/prompts/*.md` and
|
|
1728
1902
|
`agents/skills/*/SKILL.md` is under `/workspace`, `/assets`, `/skills` or
|
|
1729
1903
|
`/memory`
|
|
1730
|
-
- [ ] No prompt or skill names a project file
|
|
1904
|
+
- [ ] No prompt or skill names a project file - `agents.yaml`, a prompt path, a
|
|
1731
1905
|
skill's repository path, `sandbox/Dockerfile`, `.env`, `sessions/`
|
|
1732
1906
|
- [ ] No `../`, `~`, or host path; nothing assumes the workspace is the project
|
|
1733
1907
|
- [ ] Every `/assets/…` and `/skills/<name>/…` path names a committed file
|
|
1734
1908
|
|
|
1735
1909
|
**Skills**
|
|
1736
1910
|
|
|
1737
|
-
- [ ] Every skill is `<name>/SKILL.md`
|
|
1911
|
+
- [ ] Every skill is `<name>/SKILL.md` - no bare `<name>.md` in the catalog
|
|
1738
1912
|
- [ ] Every skill has a `description` that says _when it is needed_
|
|
1739
1913
|
- [ ] `preload` is reserved for content the model would never decline
|
|
1740
1914
|
- [ ] `preload` entries also appear in `allow` where `allow` is used
|
|
@@ -1746,11 +1920,11 @@ Before finishing any change here:
|
|
|
1746
1920
|
|
|
1747
1921
|
- [ ] Every agent holds the narrowest set its job needs
|
|
1748
1922
|
- [ ] An agent that only reads is not holding `write_file`, `apply_patch`,
|
|
1749
|
-
`move_file` or `delete_file`
|
|
1923
|
+
`move_file` or `delete_file` - subtract them from `workspace:*`
|
|
1750
1924
|
- [ ] `sandbox:*` is granted only where a shell is actually needed
|
|
1751
1925
|
- [ ] `sandbox.persist: true`, unless a throwaway rootfs is wanted on purpose
|
|
1752
1926
|
- [ ] The `sandbox:` image carries what the work needs, rather than the prompt
|
|
1753
|
-
installing it every run
|
|
1927
|
+
installing it every run - add it to `sandbox/Dockerfile` if `build:` is used
|
|
1754
1928
|
- [ ] `sandbox.env` lists names only, and nothing credential-shaped
|
|
1755
1929
|
- [ ] `exa:*` is granted only where the live web is actually needed, and the
|
|
1756
1930
|
prompt says when to trust it over what the model already believes
|
|
@@ -1769,44 +1943,61 @@ Before finishing any change here:
|
|
|
1769
1943
|
|
|
1770
1944
|
**Secrets**
|
|
1771
1945
|
|
|
1772
|
-
- [ ] No key literal in YAML, prompt, skill or log
|
|
1946
|
+
- [ ] No key literal in YAML, prompt, skill or log - `${VAR}` only
|
|
1773
1947
|
- [ ] `.env` is git-ignored
|
|
1774
1948
|
|
|
1949
|
+
**Specification** (only where the project has a `SPECIFICATION.md`)
|
|
1950
|
+
|
|
1951
|
+
- [ ] `.github/skills/zen-spec-sync/scripts/snapshot.sh status` does not say
|
|
1952
|
+
`tampered` - the baseline matches its own `manifest.txt`
|
|
1953
|
+
- [ ] `.spec-sync/baseline/applied.txt` names the newest file in
|
|
1954
|
+
`.spec-sync/history/`; a baseline ahead of the history is a pass that
|
|
1955
|
+
claimed work it did not finish
|
|
1956
|
+
- [ ] The newest history entry's `Still open` is what the next pass will trust,
|
|
1957
|
+
and says so honestly - an unchanged specification line is not evidence
|
|
1958
|
+
that anything implements it
|
|
1959
|
+
- [ ] No question in `SPECIFICATION-FEEDBACK.md` has a box marked `[x]`, or
|
|
1960
|
+
exactly one answer left under it, without a matching `✅ Answered` entry -
|
|
1961
|
+
that is an answer given and never folded into the specification
|
|
1962
|
+
- [ ] `.spec-sync/` is committed and `.tmp/` is not
|
|
1963
|
+
|
|
1775
1964
|
---
|
|
1776
1965
|
|
|
1777
1966
|
## 10. Where to change what
|
|
1778
1967
|
|
|
1779
1968
|
| Symptom | Change this |
|
|
1780
1969
|
| --------------------------------------------- | ------------------------------------------------------------------- |
|
|
1781
|
-
| Wrong tone, wrong format, wrong length |
|
|
1782
|
-
| Says something forbidden |
|
|
1970
|
+
| Wrong tone, wrong format, wrong length | The house rules (all agents) or the agent prompt |
|
|
1971
|
+
| Says something forbidden | A house-rules prohibition, stated specifically |
|
|
1783
1972
|
| Ignores a rule that only applies sometimes | Move the rule into a skill with a sharp description |
|
|
1784
1973
|
| Never loads the skill it should | The skill's `description`; or `preload` it |
|
|
1785
1974
|
| Loads too much, answers slowly | `allow:`, `maxIndexEntries:`, or `discovery: search` |
|
|
1786
|
-
| Invents a number | A skill holding the figure, or a script that computes it
|
|
1787
|
-
| A skill in the catalog is never offered | It is a bare `<name>.md`; move it to `<name>/SKILL.md`
|
|
1788
|
-
| Cannot find a file its own skill names | Absolute `/skills/<name>/…` path, and `sandbox:*`
|
|
1789
|
-
| Tries to open `agents.yaml` or its own prompt | The prompt names a file no agent can see
|
|
1790
|
-
| Says a file is missing that is in the project | The workspace is not the project directory
|
|
1791
|
-
| Rewrites a whole file to change one line | A prompt line preferring `apply_patch`
|
|
1975
|
+
| Invents a number | A skill holding the figure, or a script that computes it - §3.4.1 |
|
|
1976
|
+
| A skill in the catalog is never offered | It is a bare `<name>.md`; move it to `<name>/SKILL.md` - §3.4 |
|
|
1977
|
+
| Cannot find a file its own skill names | Absolute `/skills/<name>/…` path, and `sandbox:*` - §3.4.1 |
|
|
1978
|
+
| Tries to open `agents.yaml` or its own prompt | The prompt names a file no agent can see - §2.5 |
|
|
1979
|
+
| Says a file is missing that is in the project | The workspace is not the project directory - §2.5 |
|
|
1980
|
+
| Rewrites a whole file to change one line | A prompt line preferring `apply_patch` - §3.6 |
|
|
1792
1981
|
| Edits files it should only be reading | Subtract the mutating tools, or `zen run --read-only` |
|
|
1793
1982
|
| Cannot run the build or the tests | Grant `sandbox:*`; add the toolchain to `sandbox/Dockerfile` |
|
|
1794
|
-
| Installs the same packages on every run | Put them in `sandbox/Dockerfile`, or `sandbox.persist: true`
|
|
1795
|
-
| Answers from stale knowledge of the world | Grant `web_search` + `web_read`, and say when
|
|
1796
|
-
| Cites a page it only saw the excerpt of | A prompt line: `web_read` before quoting
|
|
1797
|
-
| Every web call refuses | No Exa key: `zen key add exa`
|
|
1798
|
-
| A model or embedder refuses every call | `zen models test <ref>`
|
|
1983
|
+
| Installs the same packages on every run | Put them in `sandbox/Dockerfile`, or `sandbox.persist: true` - §3.7 |
|
|
1984
|
+
| Answers from stale knowledge of the world | Grant `web_search` + `web_read`, and say when - §3.8 |
|
|
1985
|
+
| Cites a page it only saw the excerpt of | A prompt line: `web_read` before quoting - §3.8 |
|
|
1986
|
+
| Every web call refuses | No Exa key: `zen key add exa` - `zen check` warns - §3.8 |
|
|
1987
|
+
| A model or embedder refuses every call | `zen models test <ref>` - if `blocked`, `zen models pick` - §8 |
|
|
1799
1988
|
| Answers instead of routing | Router prompt prohibition; check `handoffs:` |
|
|
1800
1989
|
| Routes to the wrong specialist | The target agents' `description:` fields |
|
|
1801
|
-
| Gets stuck in the agent it routed to | The target needs a handoff back
|
|
1990
|
+
| Gets stuck in the agent it routed to | The target needs a handoff back - §6.3 |
|
|
1802
1991
|
| Loses a detail after a handoff | Say it in the handoff; check the collapse policy |
|
|
1803
|
-
| Works through N independent items serially | `fork:` on that agent, and a prompt line
|
|
1992
|
+
| Works through N independent items serially | `fork:` on that agent, and a prompt line - §6.4 |
|
|
1804
1993
|
| Forks when the steps actually depend | Prompt line: branches cannot see each other |
|
|
1805
1994
|
| Slow and expensive on trivial cases | Demote that agent's model tier / reasoning effort |
|
|
1806
1995
|
| Fails only on genuinely hard cases | Promote that agent's tier, or split the hard path out |
|
|
1807
|
-
| Shows no reasoning while it works | Turn summaries on for that model
|
|
1996
|
+
| Shows no reasoning while it works | Turn summaries on for that model - §7.6 |
|
|
1808
1997
|
| Forgets across conversations | Continue the session rather than starting a new one |
|
|
1809
|
-
|
|
|
1998
|
+
| Greps `/memory` or reads `graph.json` | `agents/memory-instructions.md` is missing the block - §5.3 |
|
|
1999
|
+
| Recalls a note it cannot check or continue | Same block: it is what requires provenance on every node - §5.3 |
|
|
2000
|
+
| Breaks at load with a named path | Read the message - it names the exact key |
|
|
1810
2001
|
|
|
1811
2002
|
---
|
|
1812
2003
|
|
|
@@ -1815,32 +2006,39 @@ Before finishing any change here:
|
|
|
1815
2006
|
- **The mega-prompt.** 300 lines covering twelve scenarios. Split into skills.
|
|
1816
2007
|
- **Agent sprawl.** Eight agents that share one model, one tool set and one
|
|
1817
2008
|
prompt style. Collapse into one with a catalog.
|
|
1818
|
-
- **Facts in prompts.** A fee schedule inside `
|
|
1819
|
-
versioned, cannot be shared, and is paid for on every call.
|
|
2009
|
+
- **Facts in prompts.** A fee schedule inside `agents/instructions.md`. It cannot
|
|
2010
|
+
be versioned, cannot be shared, and is paid for on every call.
|
|
1820
2011
|
- **The bare skill file.** `agents/skills/<name>.md` instead of a folder. It can
|
|
1821
|
-
never grow the table or script the next revision of the rule will want
|
|
2012
|
+
never grow the table or script the next revision of the rule will want - §3.4.
|
|
1822
2013
|
- **Pointing an agent at the project.** A prompt or skill naming `agents.yaml`,
|
|
1823
|
-
`
|
|
1824
|
-
them are on any path the agent can open, so the instruction fails on
|
|
1825
|
-
that follows it
|
|
2014
|
+
`agents/instructions.md`, `agents/skills/<name>/…` or `sandbox/Dockerfile`.
|
|
2015
|
+
None of them are on any path the agent can open, so the instruction fails on
|
|
2016
|
+
the turn that follows it - §2.5.
|
|
1826
2017
|
- **Arithmetic in prose.** A skill that walks the model through a calculation it
|
|
1827
|
-
will get wrong, in a folder that could have held the script
|
|
2018
|
+
will get wrong, in a folder that could have held the script - §3.4.1.
|
|
2019
|
+
- **The unguarded store.** `memory:` turned on and the skill's
|
|
2020
|
+
`references/memory-instructions.md` never copied to
|
|
2021
|
+
`agents/memory-instructions.md`, so nothing tells the agent the store is
|
|
2022
|
+
reached only through the tools - and `zen check` passes throughout - §5.3.
|
|
2023
|
+
- **Reviewing a capability without its skill.** A project whose memory, index or
|
|
2024
|
+
sandbox was configured by an earlier pass looks finished because it loads. The
|
|
2025
|
+
obligations are in the editor skill, not in `agents.yaml` - §0.1.
|
|
1828
2026
|
- **Politeness padding.** "Please try your best to be helpful." Costs tokens,
|
|
1829
2027
|
changes nothing.
|
|
1830
2028
|
- **Commented implementation notes.** `agents.yaml` explaining how skill
|
|
1831
|
-
discovery or prompt caching works. That is this file's job
|
|
2029
|
+
discovery or prompt caching works. That is this file's job - §3.1.
|
|
1832
2030
|
- **Fixing prompts with models.** See §4.4.
|
|
1833
2031
|
- **The chatty router.** A router that answers before handing off, because its
|
|
1834
2032
|
prompt never forbade it.
|
|
1835
2033
|
- **The one-way handoff.** A specialist with no way back, so the router routes
|
|
1836
|
-
once and then never sees the conversation again
|
|
2034
|
+
once and then never sees the conversation again - §6.3.
|
|
1837
2035
|
- **Handing off for an answer.** Wanting one lookup and spending the whole
|
|
1838
|
-
conversation on it. That is a `fork:`, which returns by itself
|
|
2036
|
+
conversation on it. That is a `fork:`, which returns by itself - §6.4.
|
|
1839
2037
|
- **Forking a chain.** Branches never see each other, so a fork whose second
|
|
1840
2038
|
branch needs the first branch's answer is a sequence wearing a fork's clothes.
|
|
1841
2039
|
- **Installing the toolchain every run.** A prompt that begins with `apt-get
|
|
1842
|
-
install` is an `image:` that was never set
|
|
1843
|
-
- **Volatile prefix.** "Current date: …" in
|
|
2040
|
+
install` is an `image:` that was never set - §3.7.
|
|
2041
|
+
- **Volatile prefix.** "Current date: …" in the house rules. Permanent cache
|
|
1844
2042
|
miss.
|
|
1845
2043
|
|
|
1846
2044
|
---
|
|
@@ -1849,16 +2047,18 @@ install` is an `image:` that was never set — §3.7.
|
|
|
1849
2047
|
|
|
1850
2048
|
This file is loaded on every request in this repository, so it is subject to its
|
|
1851
2049
|
own rules: stable, factual, no hedging. Before adding a section, ask whether it
|
|
1852
|
-
is true of every task
|
|
2050
|
+
is true of every task - if it is not, it is a skill under `.github/skills/`, or
|
|
1853
2051
|
a prompt file under `.github/prompts/` if it is a procedure (§2.4).
|
|
1854
2052
|
|
|
1855
2053
|
Update it when:
|
|
1856
2054
|
|
|
1857
2055
|
- the layout changes (a new directory, a moved catalog)
|
|
1858
|
-
- a model alias is added, retired or repointed
|
|
1859
|
-
- a vendor trap is discovered
|
|
1860
|
-
- a recurring review comment appears twice
|
|
1861
|
-
- a runtime capability is added
|
|
2056
|
+
- a model alias is added, retired or repointed - §7.2 must match `agents.yaml`
|
|
2057
|
+
- a vendor trap is discovered - add it to §7.7
|
|
2058
|
+
- a recurring review comment appears twice - turn it into a checklist line in §9
|
|
2059
|
+
- a runtime capability is added - describe it here, or it will not be used
|
|
2060
|
+
- an editor skill starts requiring a block in the target project - name the
|
|
2061
|
+
requirement in §0.1 and the check for it in §9, or it will be missed
|
|
1862
2062
|
|
|
1863
2063
|
Do not let it grow without pruning. When a section is only true of one agent, it
|
|
1864
2064
|
belongs in that agent's prompt, not here.
|