@zenera/cli 1.1.22 → 1.1.24
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 +72 -2
- package/dist/archive.d.ts +139 -0
- package/dist/archive.js +376 -0
- package/dist/ask.d.ts +51 -0
- package/dist/ask.js +143 -0
- package/dist/batch.d.ts +77 -0
- package/dist/batch.js +338 -0
- package/dist/command.d.ts +6 -0
- package/dist/commands/export.d.ts +3 -0
- package/dist/commands/export.js +255 -0
- package/dist/commands/import.d.ts +3 -0
- package/dist/commands/import.js +173 -0
- package/dist/commands/index.d.ts +5 -1
- package/dist/commands/index.js +13 -1
- package/dist/commands/inspect.d.ts +11 -0
- package/dist/commands/inspect.js +388 -89
- package/dist/commands/list.js +128 -19
- package/dist/commands/memory.js +453 -16
- package/dist/commands/models.js +14 -2
- package/dist/commands/run.js +139 -34
- package/dist/commands/sandbox.js +72 -1
- package/dist/engine.d.ts +19 -0
- package/dist/engine.js +60 -8
- package/dist/liveness.js +6 -3
- package/dist/main.js +5 -2
- package/dist/podman.d.ts +16 -0
- package/dist/podman.js +115 -2
- package/dist/progress.d.ts +33 -0
- package/dist/progress.js +402 -0
- package/dist/request.d.ts +45 -0
- package/dist/request.js +300 -0
- package/dist/sandbox.js +10 -0
- package/dist/scaffold.d.ts +8 -0
- package/dist/scaffold.js +4 -1
- package/dist/session.d.ts +26 -1
- package/dist/session.js +42 -3
- package/dist/term.d.ts +3 -0
- package/dist/term.js +17 -1
- package/dist/trace.d.ts +84 -0
- package/dist/trace.js +686 -0
- package/dist/validate.js +93 -4
- package/package.json +12 -6
- package/templates/editor/.github/copilot-instructions.md +11 -6
- package/templates/editor/.github/prompts/finetune.prompt.md +251 -0
- package/templates/editor/.github/prompts/spec-sync-project.prompt.md +25 -2
- package/templates/editor/.github/skills/zen-cli/SKILL.md +14 -3
- package/templates/editor/.github/skills/zen-cli/references/check.md +13 -5
- package/templates/editor/.github/skills/zen-cli/references/export.md +107 -0
- package/templates/editor/.github/skills/zen-cli/references/inspect.md +262 -21
- package/templates/editor/.github/skills/zen-cli/references/memory.md +123 -15
- package/templates/editor/.github/skills/zen-cli/references/models.md +1 -1
- package/templates/editor/.github/skills/zen-cli/references/projects.md +12 -5
- package/templates/editor/.github/skills/zen-cli/references/rag.md +6 -0
- package/templates/editor/.github/skills/zen-cli/references/run.md +201 -4
- package/templates/editor/.github/skills/zen-finetune/SKILL.md +1000 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/collect.sh +432 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/sample.sh +199 -0
- package/templates/editor/.github/skills/zen-inspect/SKILL.md +396 -0
- package/templates/editor/.github/skills/zen-memory/SKILL.md +74 -28
- package/templates/editor/.github/skills/zen-memory-warmup/SKILL.md +370 -408
- package/templates/editor/.github/skills/zen-sandbox/SKILL.md +19 -0
- package/templates/editor/.github/skills/zen-sandbox-capacity/SKILL.md +334 -0
- package/templates/editor/.github/skills/zen-sandbox-capacity/references/loadtest.md +181 -0
- package/templates/editor/.github/skills/zen-sandbox-capacity/scripts/preflight_sandbox.sh +457 -0
- package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +6 -5
- package/templates/editor/.vscode/agents.schema.json +1 -1
- package/templates/editor/.vscode/settings.json +1 -0
- package/templates/project/SPECIFICATION.md.tmpl +2 -0
- package/templates/project/agents/files-instructions.md +13 -0
- package/templates/project/agents/fork-instructions.md +27 -0
- package/templates/project/agents/memory-instructions.md +80 -7
- package/templates/project/gitignore +3 -0
- package/templates/project/scripts/_setup.sh +49 -7
|
@@ -1,138 +1,212 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: zen-memory-warmup
|
|
3
|
-
description: Pre-populating an agent project's memory graph
|
|
3
|
+
description: Pre-populating an agent project's memory graph before production by running a curated file of seed queries through `zen run batch` — one parallel batch instead of a hand-rolled fan-out, with per-item workspaces and per-item memories the command makes for you, choosing the source graph with --memory, bounding the parallelism with --concurrency, warming with a stronger --model, reading what each query learned from batch.json and per-item output.json, folding it all back with zen memory merge, validating with zen memory stats, atomic promotion into the project, and generating the population script in scripts/memory_warmup.sh.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Memory Warmup
|
|
7
7
|
|
|
8
|
-
Memory warmup is the process of pre-populating an agent project's knowledge
|
|
9
|
-
before deploying it or exposing it to end users.
|
|
8
|
+
Memory warmup is the process of pre-populating an agent project's knowledge
|
|
9
|
+
graph before deploying it or exposing it to end users.
|
|
10
10
|
|
|
11
|
-
In Zenera Neo
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
In Zenera Neo an agent's memory graph (`memory/`) starts empty. A cold start
|
|
12
|
+
means early runs cannot recall prior approaches, domain facts, verified plans
|
|
13
|
+
or tool schemas. Warming up addresses this by asking a curated set of
|
|
14
|
+
representative domain questions ahead of time, so the agents synthesise, link
|
|
15
|
+
and commit high-value knowledge nodes before a user is waiting on them.
|
|
16
|
+
|
|
17
|
+
Warmup is one command: **`zen run batch`**. A file of questions goes in, a
|
|
18
|
+
directory of memories comes out, and `zen memory merge` folds them into one.
|
|
16
19
|
|
|
17
20
|
---
|
|
18
21
|
|
|
19
22
|
## Why Warm Up Memory?
|
|
20
23
|
|
|
21
|
-
1. **Eliminate
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
2. **
|
|
25
|
-
a
|
|
26
|
-
model.
|
|
27
|
-
|
|
28
|
-
3. **Consistency
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
1. **Eliminate cold starts.** Instead of struggling through initial discovery,
|
|
25
|
+
the agent recalls established patterns, known schemas and proven plans on
|
|
26
|
+
its very first user-facing turn.
|
|
27
|
+
2. **A better graph, from a stronger model.** Warmup runs are infrequent, so
|
|
28
|
+
they can afford a flagship reasoning model (`--model`) that the day-to-day
|
|
29
|
+
runtime model cannot. The superior structure it leaves behind is then
|
|
30
|
+
recalled by the cheaper model.
|
|
31
|
+
3. **Consistency and alignment.** Conventions, domain boundaries and operating
|
|
32
|
+
rules are seeded as durable `fact` and `plan` nodes, rather than being
|
|
33
|
+
rediscovered inconsistently across random user sessions.
|
|
31
34
|
|
|
32
35
|
---
|
|
33
36
|
|
|
34
|
-
##
|
|
37
|
+
## What the Command Already Does
|
|
38
|
+
|
|
39
|
+
Warmup used to be a script's problem: a memory per run because the lock is per
|
|
40
|
+
directory, a fresh workspace per run, a bounded number of background jobs, and
|
|
41
|
+
a merge at the end. `zen run batch` is that fan-out made into a command, so
|
|
42
|
+
none of it is the script's problem any more.
|
|
43
|
+
|
|
44
|
+
| What warmup needs | What `zen run batch` does |
|
|
45
|
+
| :----------------------------------- | :-------------------------------------------------------------------------- |
|
|
46
|
+
| A memory no other run is writing | Each item gets its own copy at `<batch-dir>/<id>/memory` |
|
|
47
|
+
| The project's own graph left alone | It is the source to copy from, and is never written |
|
|
48
|
+
| A clean working directory per query | Each item gets `<batch-dir>/<id>/workspace` |
|
|
49
|
+
| Parallelism the machine can stand | `--concurrency <n>`, default 16, ceiling 32 |
|
|
50
|
+
| One bad query not costing the others | The failure is written to its `output.json`; the rest still answer |
|
|
51
|
+
| A record of what happened | `batch.json`, the index: every item, whether it worked, where its answer is |
|
|
52
|
+
| The fold back | It prints the `zen memory merge <batch-dir>/*/memory` line when it is done |
|
|
53
|
+
|
|
54
|
+
What is left for the warmup script is the part that is actually about warmup:
|
|
55
|
+
**which questions to ask, which graph to start from, which model to ask with,
|
|
56
|
+
and whether the result is good enough to promote.**
|
|
57
|
+
|
|
58
|
+
---
|
|
35
59
|
|
|
36
|
-
|
|
60
|
+
## Core Safety Rules
|
|
37
61
|
|
|
38
|
-
### 1. Never warm up
|
|
62
|
+
### 1. Never warm up into the project's active `memory/`
|
|
39
63
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
64
|
+
A batch never writes the source graph - it copies it - so the default is
|
|
65
|
+
already safe. Keep the whole staging tree in `.tmp/` under a timestamp so that
|
|
66
|
+
repeated or concurrent warmups cannot collide, and so a failed one can be read
|
|
67
|
+
afterwards rather than being lost:
|
|
44
68
|
|
|
45
69
|
```sh
|
|
46
70
|
STAMP="$(date +%Y%m%d-%H%M%S)"
|
|
47
|
-
--
|
|
71
|
+
zen run batch --input cases.json --batch-dir ".tmp/warmup-$STAMP/batch"
|
|
48
72
|
```
|
|
49
73
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Warmup queries often instruct agents to investigate repositories, write test
|
|
53
|
-
scripts, or run commands. Running them in the actual project or repository
|
|
54
|
-
working directory risks modifying tracked files or creating untracked clutter.
|
|
55
|
-
|
|
56
|
-
Always isolate the workspace per run into a fresh, timestamped subdirectory
|
|
57
|
-
under `.tmp/`:
|
|
74
|
+
Without `--batch-dir` the batch picks `<project>/batches/<stamp>` and prints
|
|
75
|
+
the path on stdout, which is the other way to write it:
|
|
58
76
|
|
|
59
77
|
```sh
|
|
60
|
-
--
|
|
78
|
+
DIR="$(zen run batch --input cases.json)"
|
|
61
79
|
```
|
|
62
80
|
|
|
63
|
-
|
|
64
|
-
|
|
81
|
+
### 2. Choose the graph every item starts from
|
|
82
|
+
|
|
83
|
+
`--memory <dir>` names the source. There are two warmups, and they differ only
|
|
84
|
+
in what they point it at:
|
|
65
85
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
86
|
+
| Warmup | Source | Every item starts from |
|
|
87
|
+
| :------------------------ | :-------------------------------- | :-------------------------- |
|
|
88
|
+
| Building on what is known | the project's `memory/` (default) | a copy of the current graph |
|
|
89
|
+
| From nothing | a directory that does not exist | an empty graph |
|
|
70
90
|
|
|
71
|
-
|
|
91
|
+
A source that is not there yet is not an error in the copying mode: a project
|
|
92
|
+
declares its memory directory and the first run makes it, so a cold project is
|
|
93
|
+
warmed by copying from nothing, and nothing is what each item gets.
|
|
94
|
+
|
|
95
|
+
**`--memory-read-only` is not a warmup flag.** It means nobody writes, which is
|
|
96
|
+
exactly what a warmup is for. It is for asking a batch of questions _of_ a
|
|
97
|
+
warmed graph afterwards - sixteen runs recalling from the one memory, changing
|
|
98
|
+
nothing.
|
|
99
|
+
|
|
100
|
+
### 3. Use a stronger model during population
|
|
72
101
|
|
|
73
102
|
Day-to-day execution might use a fast, cost-effective model (e.g.
|
|
74
|
-
`google:gemini-2.5-flash` or `anthropic:claude-3-5-haiku`).
|
|
75
|
-
|
|
76
|
-
(e.g. `anthropic:claude-sonnet-4-5`, `openai:gpt-5`, or
|
|
77
|
-
`google:gemini-3.5-flash`):
|
|
103
|
+
`google:gemini-2.5-flash` or `anthropic:claude-3-5-haiku`). Warm up with a
|
|
104
|
+
flagship reasoning model instead:
|
|
78
105
|
|
|
79
106
|
```sh
|
|
80
107
|
--model anthropic:claude-sonnet-4-5
|
|
81
108
|
```
|
|
82
109
|
|
|
83
|
-
|
|
84
|
-
utilized by the runtime model.
|
|
85
|
-
|
|
86
|
-
### 4. Execute non-interactively with `--json`
|
|
110
|
+
It applies to every item in the batch.
|
|
87
111
|
|
|
88
|
-
|
|
112
|
+
### 4. Read the result before promoting it
|
|
89
113
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
- The population script can programmatically verify whether the run finished
|
|
95
|
-
successfully (`stopReason === "final"`).
|
|
114
|
+
The staging graph is inspected with `zen memory stats --dir ...`, and nothing
|
|
115
|
+
overwrites the project's `memory/` until it holds what it should. Because each
|
|
116
|
+
item kept its own memory, this can be done **per query** as well as in total -
|
|
117
|
+
see below.
|
|
96
118
|
|
|
97
|
-
### 5.
|
|
119
|
+
### 5. Promote atomically, and keep the old graph
|
|
98
120
|
|
|
99
|
-
|
|
100
|
-
warmup
|
|
101
|
-
|
|
102
|
-
nodes and vector embeddings.
|
|
121
|
+
Back the current `memory/` up under a timestamp and move the staging graph into
|
|
122
|
+
place. A warmup that turns out to have been a bad idea is then one `mv` away
|
|
123
|
+
from being undone.
|
|
103
124
|
|
|
104
125
|
---
|
|
105
126
|
|
|
106
|
-
## The Warmup
|
|
127
|
+
## The Shape of a Warmup
|
|
128
|
+
|
|
129
|
+
```mermaid
|
|
130
|
+
flowchart LR
|
|
131
|
+
Q["scripts/warmup-queries.json"] --> S["select: category, shuffle, limit"]
|
|
132
|
+
S --> C["cases.json"]
|
|
133
|
+
C --> B["zen run batch --concurrency 4 --model <flagship>"]
|
|
134
|
+
B --> I1["batch/architecture-01/memory"]
|
|
135
|
+
B --> I2["batch/workflows-01/memory"]
|
|
136
|
+
B --> I3["batch/.../memory"]
|
|
137
|
+
I1 --> M["zen memory merge batch/*/memory --dir memory"]
|
|
138
|
+
I2 --> M
|
|
139
|
+
I3 --> M
|
|
140
|
+
M --> V["zen memory stats --dir memory"]
|
|
141
|
+
V --> P["back up and promote to memory/"]
|
|
142
|
+
```
|
|
107
143
|
|
|
108
144
|
```mermaid
|
|
109
145
|
flowchart TD
|
|
110
|
-
A[Curated
|
|
111
|
-
B --> C
|
|
112
|
-
C --> D["
|
|
113
|
-
|
|
114
|
-
E
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
J -- Yes --> K["Backup existing memory & promote .tmp/warmup-<timestamp>/memory to memory/"]
|
|
123
|
-
J -- No --> L[Inspect with zen memory ls / export & discard]
|
|
146
|
+
A["Curated seed queries"] --> B["Select: --category / --shuffle / --limit"]
|
|
147
|
+
B --> C{"--dry-run?"}
|
|
148
|
+
C -- Yes --> D["Print the cases file and the command, write nothing, exit 0"]
|
|
149
|
+
C -- No --> E["Write .tmp/warmup-<stamp>/cases.json"]
|
|
150
|
+
E --> F["zen run batch --input cases.json --batch-dir .tmp/warmup-<stamp>/batch"]
|
|
151
|
+
F --> G{"Exit code"}
|
|
152
|
+
G -- Non-zero --> H["Name the failed ids from batch.json, promote nothing"]
|
|
153
|
+
G -- Zero --> I["zen memory merge batch/*/memory --dir .tmp/warmup-<stamp>/memory"]
|
|
154
|
+
I --> J["zen memory stats --dir .tmp/warmup-<stamp>/memory"]
|
|
155
|
+
J --> K{"Graph healthy?"}
|
|
156
|
+
K -- No --> L["Inspect per item, or with zen memory ls / export, and discard"]
|
|
157
|
+
K -- Yes --> M["Back up memory/ and promote"]
|
|
124
158
|
```
|
|
125
159
|
|
|
126
160
|
---
|
|
127
161
|
|
|
128
|
-
##
|
|
162
|
+
## The Queries File
|
|
163
|
+
|
|
164
|
+
Keep the seed queries in the repository, in the shape the command already
|
|
165
|
+
reads, so warmup is reproducible across environments and model upgrades:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"batch": [
|
|
170
|
+
{
|
|
171
|
+
"id": "architecture-layout",
|
|
172
|
+
"input": "Investigate the project architecture and summarize the main components and data flow."
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"id": "architecture-boundaries",
|
|
176
|
+
"input": "Analyze module boundaries, dependency graphs, and core interfaces."
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"id": "workflows-triage",
|
|
180
|
+
"input": "Formulate standard operational plans for triaging and resolving common domain tasks."
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "integrations-services",
|
|
184
|
+
"input": "Identify external services, APIs, and key credential dependencies."
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"id": "errors-handling",
|
|
188
|
+
"input": "Analyze error handling strategies, recovery routines, and failure modes."
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"id": "conventions-house-rules",
|
|
192
|
+
"input": "Document project-specific coding standards, house rules, and commit policies."
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
An item takes `input`, an optional `id` and an optional `workspace`, and
|
|
199
|
+
nothing else. `memory`, `model` and `project` belong to the whole batch, so
|
|
200
|
+
they are flags on the line rather than keys in the file.
|
|
129
201
|
|
|
130
|
-
|
|
131
|
-
|
|
202
|
+
**The `id` is the category.** It names the directory the item's answer and
|
|
203
|
+
memory land in, so `architecture-layout` makes `batch/architecture-layout/` -
|
|
204
|
+
readable in a directory listing, greppable in `batch.json`, and selectable by
|
|
205
|
+
prefix. That is the whole of the category mechanism; no extra key is needed.
|
|
132
206
|
|
|
133
207
|
### Recommended Categories
|
|
134
208
|
|
|
135
|
-
| Category | Objective | Example
|
|
209
|
+
| Category | Objective | Example prompt |
|
|
136
210
|
| :------------- | :----------------------------------------------------------- | :------------------------------------------------------------------------- |
|
|
137
211
|
| `architecture` | Map high-level layout, component boundaries, and data flow | "Investigate project architecture and summarize component flow." |
|
|
138
212
|
| `workflows` | Formulate multi-step operational plans for common tasks | "Formulate standard operational plans for triaging common tasks." |
|
|
@@ -140,415 +214,303 @@ an unstructured flat list of ad-hoc prompts.
|
|
|
140
214
|
| `errors` | Capture error handling, recovery routines, and failure modes | "Analyze error handling strategies, recovery routines, and failure modes." |
|
|
141
215
|
| `conventions` | Record coding styles, house rules, and constraints | "Document project-specific coding standards and house rules." |
|
|
142
216
|
|
|
143
|
-
### Why
|
|
217
|
+
### Why Selection Still Matters
|
|
218
|
+
|
|
219
|
+
Everything runs at once now, so ordering no longer decides what gets asked -
|
|
220
|
+
but a budget still does.
|
|
221
|
+
|
|
222
|
+
1. **Balanced coverage.** A 20-query suite cut to the first four with a limit
|
|
223
|
+
would warm `architecture` and leave `workflows` and `integrations` cold.
|
|
224
|
+
Shuffle before limiting so a partial budget draws a cross-section.
|
|
225
|
+
2. **`--category <name>`** re-warms one subsystem after it changes - the
|
|
226
|
+
`integrations` queries alone once a new API is wired up - without paying for
|
|
227
|
+
the rest.
|
|
228
|
+
3. **`--limit <n>`** caps what a verification pass costs. With sixteen in
|
|
229
|
+
flight the wall clock stops being the thing that limits you, so the cap has
|
|
230
|
+
to be deliberate.
|
|
231
|
+
4. **`--dry-run`** prints the selected cases file and the exact command, writes
|
|
232
|
+
nothing, creates nothing, and bills nothing. Frontier models are expensive
|
|
233
|
+
enough that the plan is worth reading first.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Reading What Each Query Learned
|
|
238
|
+
|
|
239
|
+
Because every item kept its own memory, a warmup is auditable per question -
|
|
240
|
+
which a sequential run into one graph never was.
|
|
144
241
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
4. **`--limit <n>`**: Prevents unintended token expenditure by capping the total
|
|
155
|
-
number of queries executed during test or verification passes.
|
|
242
|
+
```sh
|
|
243
|
+
DIR=".tmp/warmup-$STAMP/batch"
|
|
244
|
+
|
|
245
|
+
# Which items worked, and what each was asked
|
|
246
|
+
jq -r '.batch_results[] | "\(.id)\t\(if .ok then "ok" else "FAILED" end)"' "$DIR/batch.json"
|
|
247
|
+
|
|
248
|
+
# What one query actually committed
|
|
249
|
+
zen memory stats --dir "$DIR/architecture-layout/memory"
|
|
250
|
+
zen memory ls --dir "$DIR/architecture-layout/memory"
|
|
156
251
|
|
|
157
|
-
|
|
252
|
+
# What one query answered, and what it cost
|
|
253
|
+
jq '.stopReason, .usage' "$DIR/architecture-layout/output.json"
|
|
254
|
+
```
|
|
158
255
|
|
|
159
|
-
|
|
160
|
-
|
|
256
|
+
A query that taught the graph nothing useful is excluded by leaving its
|
|
257
|
+
directory out of the merge. Nothing else has to be re-run.
|
|
161
258
|
|
|
162
|
-
|
|
163
|
-
-
|
|
164
|
-
shuffling, and limits.
|
|
165
|
-
- Checking the exact `zen run ...` commands that would be executed.
|
|
166
|
-
- Guaranteeing that zero files are written, zero directories are created, and
|
|
167
|
-
zero model tokens are billed.
|
|
259
|
+
An item that committed nothing leaves no `memory/` at all, so
|
|
260
|
+
`<batch-dir>/*/memory` never names a directory `merge` would refuse.
|
|
168
261
|
|
|
169
262
|
---
|
|
170
263
|
|
|
171
264
|
## Generating the Warmup Script in `scripts/`
|
|
172
265
|
|
|
173
|
-
A project should
|
|
174
|
-
(e.g. `scripts/memory_warmup.sh` or `scripts/memory-warmup.sh`).
|
|
175
|
-
|
|
176
|
-
The script should automate the entire pipeline:
|
|
177
|
-
|
|
178
|
-
1. Accept command-line flags:
|
|
179
|
-
- `--dry-run`: Preview selected queries, categories, and commands without executing.
|
|
180
|
-
- `--limit <n>`: Run at most $n$ queries.
|
|
181
|
-
- `--shuffle`: Randomize query order before applying limits.
|
|
182
|
-
- `--category <name>`: Filter queries to a specific category.
|
|
183
|
-
- `--model <provider:model>`: Override model with a reasoning flagship.
|
|
184
|
-
- `--incremental`: Copy existing project memory into staging before warming up.
|
|
185
|
-
- `-y`, `--yes`: Promote memory without interactive confirmation.
|
|
186
|
-
2. Create timestamped staging paths under `.tmp/` (e.g.
|
|
187
|
-
`.tmp/warmup-$(date +%Y%m%d-%H%M%S)/`) and timestamped per-query workspaces.
|
|
188
|
-
3. Filter, shuffle, and sample from categorized query definitions.
|
|
189
|
-
4. If `--dry-run` is active, print the execution plan and exit 0 before touching disk.
|
|
190
|
-
5. Invoke `zen run` with `--memory`, `--workspace`, `--model`, and `--json`.
|
|
191
|
-
6. Parse each outcome with `jq`, halt if a turn fails, and report per-query execution time and total elapsed time since start.
|
|
192
|
-
7. Print graph statistics via `zen memory stats --dir ...` between queries to track graph growth, followed by a final validation.
|
|
193
|
-
8. Safely backup and replace the project's `memory/` directory with the warmed graph.
|
|
266
|
+
A project should keep its warmup under version control as two files:
|
|
194
267
|
|
|
195
|
-
|
|
268
|
+
- `scripts/warmup-queries.json` - the curated seed queries, in the shape above.
|
|
269
|
+
- `scripts/memory_warmup.sh` - selection, one batch, merge, validate, promote.
|
|
270
|
+
|
|
271
|
+
The script should:
|
|
196
272
|
|
|
197
|
-
|
|
198
|
-
|
|
273
|
+
1. Accept `--category`, `--limit`, `--shuffle`, `--concurrency`, `--model`,
|
|
274
|
+
`--fresh`, `--dry-run` and `-y/--yes`.
|
|
275
|
+
2. Select from the queries file and write the selection to a timestamped
|
|
276
|
+
staging directory under `.tmp/`.
|
|
277
|
+
3. Stop before touching disk under `--dry-run`, having printed the plan.
|
|
278
|
+
4. Run **one** `zen run batch`, letting it own the parallelism, the per-item
|
|
279
|
+
workspaces and the per-item memories.
|
|
280
|
+
5. Refuse to promote anything if any item failed, naming the ids from
|
|
281
|
+
`batch.json` and leaving the staging tree for inspection.
|
|
282
|
+
6. Merge the item memories, print `zen memory stats`, then back up and promote.
|
|
283
|
+
|
|
284
|
+
### Recommended Script Template (`scripts/memory_warmup.sh`)
|
|
199
285
|
|
|
200
286
|
```bash
|
|
201
287
|
#!/usr/bin/env bash
|
|
202
288
|
#
|
|
203
|
-
# scripts/memory_warmup.sh —
|
|
289
|
+
# scripts/memory_warmup.sh — Warm this project's memory with one parallel batch.
|
|
204
290
|
#
|
|
205
291
|
# Usage:
|
|
206
292
|
# ./scripts/memory_warmup.sh [options]
|
|
207
293
|
#
|
|
208
294
|
# Options:
|
|
209
|
-
# --
|
|
210
|
-
# --limit <n>
|
|
211
|
-
# --shuffle
|
|
212
|
-
# --
|
|
213
|
-
# --model <ref>
|
|
214
|
-
# --
|
|
215
|
-
# -
|
|
216
|
-
# -
|
|
295
|
+
# --category <name> Only queries whose id starts with "<name>-"
|
|
296
|
+
# --limit <n> Run at most n queries
|
|
297
|
+
# --shuffle Randomize order before applying --limit
|
|
298
|
+
# --concurrency <n> How many at a time (default 4; zen allows up to 32)
|
|
299
|
+
# --model <ref> Warm up with a stronger model than the runtime one
|
|
300
|
+
# --fresh Start every item from an empty graph, not from memory/
|
|
301
|
+
# --dry-run Print the plan and exit; write nothing, bill nothing
|
|
302
|
+
# -y, --yes Promote without asking
|
|
303
|
+
# -h, --help Show this help
|
|
217
304
|
#
|
|
218
305
|
# Examples:
|
|
219
306
|
# ./scripts/memory_warmup.sh --dry-run
|
|
220
|
-
# ./scripts/memory_warmup.sh --
|
|
307
|
+
# ./scripts/memory_warmup.sh --category integrations
|
|
221
308
|
# ./scripts/memory_warmup.sh --shuffle --limit 5 --model anthropic:claude-sonnet-4-5
|
|
222
|
-
# ./scripts/memory_warmup.sh --
|
|
309
|
+
# ./scripts/memory_warmup.sh --fresh -y
|
|
223
310
|
#
|
|
224
311
|
|
|
225
312
|
set -euo pipefail
|
|
226
313
|
|
|
227
|
-
# Find project root regardless of invocation directory
|
|
228
314
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
229
315
|
cd "$ROOT"
|
|
230
316
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
DRY_RUN=false
|
|
317
|
+
QUERIES="scripts/warmup-queries.json"
|
|
318
|
+
PROJECT_MEM="memory"
|
|
319
|
+
|
|
320
|
+
CATEGORY=""
|
|
236
321
|
LIMIT=0
|
|
237
322
|
SHUFFLE=false
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if [ $hours -gt 0 ]; then
|
|
247
|
-
echo "${hours}h ${mins}m ${secs}s"
|
|
248
|
-
elif [ $mins -gt 0 ]; then
|
|
249
|
-
echo "${mins}m ${secs}s"
|
|
250
|
-
else
|
|
251
|
-
echo "${secs}s"
|
|
252
|
-
fi
|
|
323
|
+
CONCURRENCY=4
|
|
324
|
+
MODEL="${ZEN_WARMUP_MODEL:-}"
|
|
325
|
+
FRESH=false
|
|
326
|
+
DRY_RUN=false
|
|
327
|
+
ASSUME_YES=false
|
|
328
|
+
|
|
329
|
+
usage() {
|
|
330
|
+
sed -n '2,25p' "$0" | sed 's/^# \{0,1\}//'
|
|
253
331
|
}
|
|
254
332
|
|
|
255
|
-
# Parse arguments
|
|
256
333
|
while [[ $# -gt 0 ]]; do
|
|
257
334
|
case "$1" in
|
|
258
|
-
--
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
--
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
shift
|
|
269
|
-
;;
|
|
270
|
-
--category)
|
|
271
|
-
CATEGORY_FILTER="$2"
|
|
272
|
-
shift 2
|
|
273
|
-
;;
|
|
274
|
-
--model)
|
|
275
|
-
MODEL="$2"
|
|
276
|
-
shift 2
|
|
277
|
-
;;
|
|
278
|
-
--incremental)
|
|
279
|
-
INCREMENTAL=true
|
|
280
|
-
shift
|
|
281
|
-
;;
|
|
282
|
-
-y|--yes)
|
|
283
|
-
ASSUME_YES=true
|
|
284
|
-
shift
|
|
285
|
-
;;
|
|
286
|
-
-h|--help)
|
|
287
|
-
echo "Usage: ./scripts/memory_warmup.sh [options]"
|
|
288
|
-
echo ""
|
|
289
|
-
echo "Options:"
|
|
290
|
-
echo " --dry-run Preview selected queries and commands without executing"
|
|
291
|
-
echo " --limit <n> Maximum number of queries to run"
|
|
292
|
-
echo " --shuffle Randomize query order before applying limits"
|
|
293
|
-
echo " --category <name> Filter queries to a specific category"
|
|
294
|
-
echo " --model <ref> Override model (e.g. anthropic:claude-sonnet-4-5)"
|
|
295
|
-
echo " --incremental Seed staging memory with current memory/ before runs"
|
|
296
|
-
echo " -y, --yes Assume yes for memory promotion"
|
|
297
|
-
echo " -h, --help Show this help"
|
|
298
|
-
exit 0
|
|
299
|
-
;;
|
|
300
|
-
*)
|
|
301
|
-
echo "Error: Unknown option $1" >&2
|
|
302
|
-
exit 2
|
|
303
|
-
;;
|
|
335
|
+
--category) CATEGORY="$2"; shift 2 ;;
|
|
336
|
+
--limit) LIMIT="$2"; shift 2 ;;
|
|
337
|
+
--shuffle) SHUFFLE=true; shift ;;
|
|
338
|
+
--concurrency) CONCURRENCY="$2"; shift 2 ;;
|
|
339
|
+
--model) MODEL="$2"; shift 2 ;;
|
|
340
|
+
--fresh) FRESH=true; shift ;;
|
|
341
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
342
|
+
-y|--yes) ASSUME_YES=true; shift ;;
|
|
343
|
+
-h|--help) usage; exit 0 ;;
|
|
344
|
+
*) echo "Error: unknown option $1" >&2; exit 2 ;;
|
|
304
345
|
esac
|
|
305
346
|
done
|
|
306
347
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
STAGING_MEM="$SCRATCH_DIR/memory"
|
|
310
|
-
WORKSPACES_DIR="$SCRATCH_DIR/workspaces"
|
|
311
|
-
PROJECT_MEM="memory"
|
|
312
|
-
|
|
313
|
-
# Define curated seed queries structured by category ("category|prompt")
|
|
314
|
-
ALL_QUERIES=(
|
|
315
|
-
"architecture|Investigate the project architecture and summarize the main components and data flow."
|
|
316
|
-
"architecture|Analyze module boundaries, dependency graphs, and core interfaces."
|
|
317
|
-
"workflows|Formulate standard operational plans for triaging and resolving common domain tasks."
|
|
318
|
-
"workflows|Trace the end-to-end user request lifecycle and hand-off points."
|
|
319
|
-
"integrations|Identify external services, APIs, and key credential dependencies."
|
|
320
|
-
"integrations|Verify database and storage schemas, access patterns, and persistence rules."
|
|
321
|
-
"errors|Analyze error handling strategies, recovery routines, and failure modes."
|
|
322
|
-
"errors|Identify retry policies, rate limits, and fallback behaviors."
|
|
323
|
-
"conventions|Document project-specific coding standards, house rules, and commit policies."
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
# 1. Filter by category if requested
|
|
327
|
-
FILTERED=()
|
|
328
|
-
for item in "${ALL_QUERIES[@]}"; do
|
|
329
|
-
cat="${item%%|*}"
|
|
330
|
-
if [ -z "$CATEGORY_FILTER" ] || [ "$cat" = "$CATEGORY_FILTER" ]; then
|
|
331
|
-
FILTERED+=("$item")
|
|
332
|
-
fi
|
|
333
|
-
done
|
|
348
|
+
command -v jq >/dev/null 2>&1 || { echo "Error: jq is required." >&2; exit 1; }
|
|
349
|
+
[ -f "$QUERIES" ] || { echo "Error: $QUERIES not found." >&2; exit 1; }
|
|
334
350
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
351
|
+
# --- Select -----------------------------------------------------------------
|
|
352
|
+
# One compact JSON object per line, so ordering and limiting are line work.
|
|
353
|
+
SELECTED="$(jq -c --arg cat "$CATEGORY" \
|
|
354
|
+
'.batch[] | select($cat == "" or (.id // "" | startswith($cat + "-")))' \
|
|
355
|
+
"$QUERIES")"
|
|
339
356
|
|
|
340
|
-
# 2. Shuffle if requested
|
|
341
357
|
if [ "$SHUFFLE" = true ]; then
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
while IFS= read -r line; do
|
|
345
|
-
[ -n "$line" ] && TEMP+=("$line")
|
|
346
|
-
done < <(printf '%s\n' "${FILTERED[@]}" | python3 -c 'import sys, random; lines = [l for l in sys.stdin.read().splitlines() if l]; random.shuffle(lines); print("\n".join(lines))')
|
|
347
|
-
elif command -v shuf >/dev/null 2>&1; then
|
|
348
|
-
while IFS= read -r line; do
|
|
349
|
-
[ -n "$line" ] && TEMP+=("$line")
|
|
350
|
-
done < <(printf '%s\n' "${FILTERED[@]}" | shuf)
|
|
351
|
-
elif sort -R </dev/null >/dev/null 2>&1; then
|
|
352
|
-
while IFS= read -r line; do
|
|
353
|
-
[ -n "$line" ] && TEMP+=("$line")
|
|
354
|
-
done < <(printf '%s\n' "${FILTERED[@]}" | sort -R)
|
|
358
|
+
if command -v shuf >/dev/null 2>&1; then
|
|
359
|
+
SELECTED="$(printf '%s\n' "$SELECTED" | shuf)"
|
|
355
360
|
else
|
|
356
|
-
|
|
361
|
+
SELECTED="$(printf '%s\n' "$SELECTED" | sort -R)"
|
|
357
362
|
fi
|
|
358
|
-
FILTERED=("${TEMP[@]}")
|
|
359
363
|
fi
|
|
360
364
|
|
|
361
|
-
#
|
|
362
|
-
|
|
363
|
-
if [ "$LIMIT" -gt 0 ]
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
365
|
+
# awk rather than head: head closes the pipe early, and a SIGPIPE inside a
|
|
366
|
+
# command substitution takes the whole script down under `set -e`.
|
|
367
|
+
if [ "$LIMIT" -gt 0 ]; then
|
|
368
|
+
SELECTED="$(printf '%s\n' "$SELECTED" | awk -v n="$LIMIT" 'NR <= n')"
|
|
369
|
+
fi
|
|
370
|
+
|
|
371
|
+
COUNT="$(printf '%s\n' "$SELECTED" | awk 'NF { n++ } END { print n + 0 }')"
|
|
372
|
+
if [ "$COUNT" -eq 0 ]; then
|
|
373
|
+
echo "Error: no queries matched${CATEGORY:+ category '$CATEGORY'}." >&2
|
|
374
|
+
exit 1
|
|
375
|
+
fi
|
|
376
|
+
CASES="$(printf '%s\n' "$SELECTED" | jq -s '{ batch: . }')"
|
|
377
|
+
|
|
378
|
+
STAMP="$(date +%Y%m%d-%H%M%S)"
|
|
379
|
+
STAGE=".tmp/warmup-$STAMP"
|
|
380
|
+
BATCH_DIR="$STAGE/batch"
|
|
381
|
+
STAGING_MEM="$STAGE/memory"
|
|
382
|
+
CASES_FILE="$STAGE/cases.json"
|
|
383
|
+
|
|
384
|
+
CMD=(zen run batch --input "$CASES_FILE" --batch-dir "$BATCH_DIR"
|
|
385
|
+
--concurrency "$CONCURRENCY")
|
|
386
|
+
if [ -n "$MODEL" ]; then
|
|
387
|
+
CMD+=(--model "$MODEL")
|
|
388
|
+
fi
|
|
389
|
+
if [ "$FRESH" = true ]; then
|
|
390
|
+
# A source that does not exist yet: every item starts from an empty graph
|
|
391
|
+
# instead of a copy of what the project already knows.
|
|
392
|
+
CMD+=(--memory "$STAGE/from-nothing")
|
|
369
393
|
fi
|
|
370
394
|
|
|
371
|
-
#
|
|
395
|
+
# --- Dry run ----------------------------------------------------------------
|
|
372
396
|
if [ "$DRY_RUN" = true ]; then
|
|
373
|
-
echo "=== DRY RUN:
|
|
374
|
-
echo "Project root:
|
|
375
|
-
echo "Staging
|
|
376
|
-
echo "Target memory:
|
|
377
|
-
echo "Model
|
|
378
|
-
echo "Category
|
|
379
|
-
echo "Shuffled:
|
|
380
|
-
echo "Limit:
|
|
381
|
-
echo "
|
|
382
|
-
echo ""
|
|
383
|
-
echo "
|
|
384
|
-
for item in "${SELECTED[@]}"; do
|
|
385
|
-
cat="${item%%|*}"
|
|
386
|
-
q="${item#*|}"
|
|
387
|
-
printf ' [%-14s] %s\n' "$cat" "$q"
|
|
388
|
-
done
|
|
397
|
+
echo "=== DRY RUN: warmup plan ==="
|
|
398
|
+
echo "Project root: $ROOT"
|
|
399
|
+
echo "Staging: $STAGE"
|
|
400
|
+
echo "Target memory: $PROJECT_MEM"
|
|
401
|
+
echo "Model: ${MODEL:-<project default>}"
|
|
402
|
+
echo "Category: ${CATEGORY:-<all>}"
|
|
403
|
+
echo "Shuffled: $SHUFFLE"
|
|
404
|
+
echo "Limit: $([ "$LIMIT" -gt 0 ] && echo "$LIMIT" || echo none)"
|
|
405
|
+
echo "Concurrency: $CONCURRENCY"
|
|
406
|
+
echo "Starting from: $([ "$FRESH" = true ] && echo "an empty graph" || echo "a copy of $PROJECT_MEM")"
|
|
407
|
+
echo "Queries: $COUNT"
|
|
389
408
|
echo ""
|
|
390
|
-
|
|
391
|
-
SAMPLE_Q="${SELECTED[0]#*|}"
|
|
392
|
-
echo " zen run --memory \"$STAGING_MEM\" --workspace \"$WORKSPACES_DIR/run-1-<timestamp>\" ${MODEL:+--model \"$MODEL\" }--json \"$SAMPLE_Q\""
|
|
393
|
-
echo " zen memory stats --dir \"$STAGING_MEM\""
|
|
409
|
+
printf '%s\n' "$CASES" | jq -r '.batch[] | " [\(.id)] \(.input)"'
|
|
394
410
|
echo ""
|
|
395
|
-
echo "
|
|
396
|
-
|
|
411
|
+
echo "Would run:"
|
|
412
|
+
printf ' '; printf '%q ' "${CMD[@]}"; echo ""
|
|
413
|
+
echo " zen memory merge $BATCH_DIR/*/memory --dir $STAGING_MEM --yes"
|
|
414
|
+
echo " zen memory stats --dir $STAGING_MEM"
|
|
397
415
|
echo ""
|
|
398
|
-
echo "Dry run complete.
|
|
416
|
+
echo "Dry run complete. Nothing created, nothing billed."
|
|
399
417
|
exit 0
|
|
400
418
|
fi
|
|
401
419
|
|
|
402
|
-
|
|
420
|
+
# --- Run --------------------------------------------------------------------
|
|
421
|
+
mkdir -p "$STAGE"
|
|
422
|
+
printf '%s\n' "$CASES" >"$CASES_FILE"
|
|
403
423
|
|
|
404
|
-
echo "=== Zenera Neo
|
|
405
|
-
echo "
|
|
406
|
-
echo "
|
|
407
|
-
if [ -n "$MODEL" ]; then
|
|
408
|
-
echo "Model override: $MODEL"
|
|
409
|
-
fi
|
|
410
|
-
echo "Selected queries: ${#SELECTED[@]}"
|
|
424
|
+
echo "=== Zenera Neo memory warmup ==="
|
|
425
|
+
echo "Staging: $STAGE"
|
|
426
|
+
echo "Queries: $COUNT, $CONCURRENCY at a time"
|
|
411
427
|
echo ""
|
|
412
428
|
|
|
413
|
-
#
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
trap cleanup EXIT
|
|
423
|
-
|
|
424
|
-
# Prepare staging memory
|
|
425
|
-
mkdir -p "$STAGING_MEM" "$WORKSPACES_DIR"
|
|
426
|
-
|
|
427
|
-
if [ "$INCREMENTAL" = true ] && [ -d "$PROJECT_MEM" ]; then
|
|
428
|
-
echo "Copying existing memory for incremental warmup..."
|
|
429
|
-
cp -R "$PROJECT_MEM/." "$STAGING_MEM/"
|
|
429
|
+
# The batch prints its directory on stdout; we chose it, so we do not need it
|
|
430
|
+
# repeated. Progress and per-item results are on stderr and stay visible.
|
|
431
|
+
if ! "${CMD[@]}" >/dev/null; then
|
|
432
|
+
echo "" >&2
|
|
433
|
+
echo "Some queries failed; nothing was promoted." >&2
|
|
434
|
+
jq -r '.batch_results[] | select(.ok | not) | " \(.id): \(.error.message)"' \
|
|
435
|
+
"$BATCH_DIR/batch.json" >&2
|
|
436
|
+
echo "Staging left for inspection at: $STAGE" >&2
|
|
437
|
+
exit 1
|
|
430
438
|
fi
|
|
431
439
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
for
|
|
437
|
-
|
|
438
|
-
CATEGORY="${item%%|*}"
|
|
439
|
-
QUERY="${item#*|}"
|
|
440
|
-
|
|
441
|
-
RUN_STAMP="$(date +%s)"
|
|
442
|
-
RUN_WORKSPACE="$WORKSPACES_DIR/run-$COUNT-$RUN_STAMP"
|
|
443
|
-
mkdir -p "$RUN_WORKSPACE"
|
|
444
|
-
|
|
445
|
-
echo "[$COUNT/${#SELECTED[@]}] [$CATEGORY] \"$QUERY\""
|
|
446
|
-
echo " -> Workspace: $RUN_WORKSPACE"
|
|
447
|
-
|
|
448
|
-
# Assemble arguments
|
|
449
|
-
CMD=(zen run --memory "$STAGING_MEM" --workspace "$RUN_WORKSPACE" --json)
|
|
450
|
-
if [ -n "$MODEL" ]; then
|
|
451
|
-
CMD+=(--model "$MODEL")
|
|
452
|
-
fi
|
|
453
|
-
CMD+=("$QUERY")
|
|
454
|
-
|
|
455
|
-
QUERY_START=$(date +%s)
|
|
456
|
-
|
|
457
|
-
# Run query and capture JSON output
|
|
458
|
-
JSON_OUT="$("${CMD[@]}")"
|
|
459
|
-
EXIT_CODE=$?
|
|
460
|
-
|
|
461
|
-
QUERY_END=$(date +%s)
|
|
462
|
-
QUERY_DURATION=$((QUERY_END - QUERY_START))
|
|
463
|
-
TOTAL_ELAPSED=$((QUERY_END - WARMUP_START))
|
|
464
|
-
|
|
465
|
-
if [ $EXIT_CODE -ne 0 ]; then
|
|
466
|
-
echo " -> Command exited with code $EXIT_CODE (query: $(format_duration $QUERY_DURATION), total elapsed: $(format_duration $TOTAL_ELAPSED))" >&2
|
|
467
|
-
exit $EXIT_CODE
|
|
468
|
-
fi
|
|
469
|
-
|
|
470
|
-
# Parse outcome via jq if available
|
|
471
|
-
if command -v jq >/dev/null 2>&1; then
|
|
472
|
-
STOP_REASON=$(echo "$JSON_OUT" | jq -r '.stopReason // empty')
|
|
473
|
-
AGENT=$(echo "$JSON_OUT" | jq -r '.agent // empty')
|
|
474
|
-
echo " -> Completed: agent=$AGENT stopReason=$STOP_REASON"
|
|
475
|
-
|
|
476
|
-
if [ "$STOP_REASON" != "final" ]; then
|
|
477
|
-
echo " -> Unexpected stop reason: $STOP_REASON" >&2
|
|
478
|
-
exit 1
|
|
479
|
-
fi
|
|
480
|
-
else
|
|
481
|
-
echo " -> Turn completed successfully."
|
|
482
|
-
fi
|
|
483
|
-
|
|
484
|
-
echo " -> Timing: query took $(format_duration $QUERY_DURATION) | elapsed since start: $(format_duration $TOTAL_ELAPSED)"
|
|
485
|
-
|
|
486
|
-
# Print memory stats between queries to track graph growth
|
|
487
|
-
if [ -f "$STAGING_MEM/manifest.json" ]; then
|
|
488
|
-
echo ""
|
|
489
|
-
echo "Memory stats after query $COUNT:"
|
|
490
|
-
zen memory stats --dir "$STAGING_MEM"
|
|
491
|
-
else
|
|
492
|
-
echo " -> Memory stats: No memories committed yet."
|
|
493
|
-
fi
|
|
494
|
-
echo ""
|
|
440
|
+
# --- Merge ------------------------------------------------------------------
|
|
441
|
+
# An item that committed nothing leaves no memory behind, so this glob only
|
|
442
|
+
# ever names real graphs.
|
|
443
|
+
MERGE=()
|
|
444
|
+
for mem in "$BATCH_DIR"/*/memory; do
|
|
445
|
+
[ -d "$mem" ] && MERGE+=("$mem")
|
|
495
446
|
done
|
|
496
447
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
echo "
|
|
448
|
+
if [ ${#MERGE[@]} -eq 0 ]; then
|
|
449
|
+
echo "No query committed anything to memory; nothing to promote." >&2
|
|
450
|
+
echo "Answers are still at: $BATCH_DIR" >&2
|
|
451
|
+
exit 1
|
|
452
|
+
fi
|
|
453
|
+
|
|
500
454
|
echo ""
|
|
455
|
+
echo "Merging ${#MERGE[@]} memories into $STAGING_MEM..."
|
|
456
|
+
zen memory merge "${MERGE[@]}" --dir "$STAGING_MEM" --yes
|
|
501
457
|
|
|
502
|
-
|
|
503
|
-
echo "
|
|
458
|
+
echo ""
|
|
459
|
+
echo "Warmed graph:"
|
|
504
460
|
zen memory stats --dir "$STAGING_MEM"
|
|
505
461
|
echo ""
|
|
506
462
|
|
|
507
|
-
#
|
|
463
|
+
# --- Promote ----------------------------------------------------------------
|
|
508
464
|
if [ "$ASSUME_YES" = false ]; then
|
|
509
|
-
read -r -p "Promote
|
|
465
|
+
read -r -p "Promote this graph to '$PROJECT_MEM'? [y/N] " CONFIRM
|
|
510
466
|
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
|
|
511
|
-
echo "
|
|
467
|
+
echo "Not promoted. Staging left at $STAGE."
|
|
512
468
|
exit 0
|
|
513
469
|
fi
|
|
514
470
|
fi
|
|
515
471
|
|
|
516
|
-
# Backup existing project memory if present
|
|
517
472
|
if [ -d "$PROJECT_MEM" ]; then
|
|
518
|
-
|
|
519
|
-
echo "Backing up current memory to $
|
|
520
|
-
mv "$PROJECT_MEM" "$
|
|
473
|
+
BACKUP="memory.bak.$STAMP"
|
|
474
|
+
echo "Backing up current memory to $BACKUP..."
|
|
475
|
+
mv "$PROJECT_MEM" "$BACKUP"
|
|
521
476
|
fi
|
|
522
477
|
|
|
523
|
-
# Promote staging memory to project root
|
|
524
|
-
echo "Promoting warmed memory to $PROJECT_MEM..."
|
|
525
478
|
mv "$STAGING_MEM" "$PROJECT_MEM"
|
|
479
|
+
rm -rf "$STAGE"
|
|
526
480
|
|
|
527
|
-
|
|
528
|
-
rm -rf "$SCRATCH_DIR"
|
|
529
|
-
|
|
530
|
-
echo "Warmup complete! Project memory is ready."
|
|
481
|
+
echo "Warmup complete. Project memory is ready."
|
|
531
482
|
```
|
|
532
483
|
|
|
533
484
|
---
|
|
534
485
|
|
|
486
|
+
## When Not to Batch
|
|
487
|
+
|
|
488
|
+
A batch gives every item its own graph, so no item can recall what another one
|
|
489
|
+
just learned. When the queries genuinely build on each other - a later question
|
|
490
|
+
that only makes sense once an earlier one has committed its answer - run those
|
|
491
|
+
few in sequence with plain `zen run --memory`, and batch the rest.
|
|
492
|
+
|
|
493
|
+
`merge` gives back everything except that cross-query recall. Reach for the
|
|
494
|
+
sequence only where the recall is the point; it costs the wall clock that
|
|
495
|
+
warming in parallel is for.
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
535
499
|
## Best Practices for Seed Queries
|
|
536
500
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
- Store the seed prompts inside the script or an adjacent `scripts/warmup-queries.txt`
|
|
554
|
-
so warmup is reproducible across environments and model upgrades.
|
|
501
|
+
1. **Name ids by category.** `architecture-layout`, `workflows-triage`. The id
|
|
502
|
+
is the directory name, the selector, and the label in every report.
|
|
503
|
+
2. **Dry-run first.** Verify the selection, the model and the source graph
|
|
504
|
+
before spending a flagship model on twenty questions at once.
|
|
505
|
+
3. **Write prompts that induce plan formation.** "Analyze and determine the
|
|
506
|
+
recommended pattern for..." stores durable `plan` and `fact` nodes; "list
|
|
507
|
+
the files in src/" stores nothing worth keeping.
|
|
508
|
+
4. **Verify vectorisation.** In `zen memory stats`, the vector count should
|
|
509
|
+
equal the node count. If it does not, the embedding model is not configured
|
|
510
|
+
and recall will fall back to text alone.
|
|
511
|
+
5. **Keep the queries under version control.** `scripts/warmup-queries.json` is
|
|
512
|
+
the record of what the project's memory was built from, and the thing to
|
|
513
|
+
re-run after a model upgrade.
|
|
514
|
+
6. **Warm in one batch, not many.** A second batch cannot see what the first
|
|
515
|
+
learned unless it has been merged and promoted first - so either merge in
|
|
516
|
+
between, or ask everything at once.
|