@zenera/cli 1.1.17 → 1.1.18

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.
Files changed (50) hide show
  1. package/README.md +99 -16
  2. package/dist/banner.d.ts +19 -0
  3. package/dist/banner.js +12 -9
  4. package/dist/command.d.ts +3 -0
  5. package/dist/commands/index.js +4 -1
  6. package/dist/commands/init.js +1 -1
  7. package/dist/commands/inspect.js +77 -13
  8. package/dist/commands/key.js +49 -1
  9. package/dist/commands/memory.js +16 -3
  10. package/dist/commands/meta.d.ts +3 -0
  11. package/dist/commands/meta.js +596 -0
  12. package/dist/commands/run.js +63 -38
  13. package/dist/engine.d.ts +15 -0
  14. package/dist/engine.js +20 -1
  15. package/dist/home.d.ts +2 -0
  16. package/dist/home.js +2 -0
  17. package/dist/main.js +1 -1
  18. package/dist/meta.d.ts +221 -0
  19. package/dist/meta.js +695 -0
  20. package/dist/podman.js +64 -2
  21. package/dist/projects.d.ts +9 -1
  22. package/dist/projects.js +19 -2
  23. package/dist/resolve.js +7 -1
  24. package/dist/sandbox.d.ts +2 -0
  25. package/dist/sandbox.js +1 -1
  26. package/dist/scaffold.js +1 -1
  27. package/dist/session.d.ts +10 -0
  28. package/dist/session.js +19 -1
  29. package/dist/term.d.ts +7 -0
  30. package/dist/term.js +8 -1
  31. package/dist/tui/app.js +33 -5
  32. package/dist/tui/wrap.d.ts +1 -1
  33. package/dist/tui/wrap.js +1 -1
  34. package/package.json +3 -2
  35. package/templates/editor/.github/copilot-instructions.md +59 -5
  36. package/templates/editor/.github/prompts/{review-project.prompt.md → project-review.prompt.md} +0 -1
  37. package/templates/editor/.github/prompts/{update-spec-from-feedback.prompt.md → spec-apply-feedback.prompt.md} +5 -5
  38. package/templates/editor/.github/prompts/{sync-with-spec.prompt.md → spec-sync-project.prompt.md} +4 -4
  39. package/templates/editor/.github/skills/zen-cli/references/inspect.md +4 -3
  40. package/templates/editor/.github/skills/zen-cli/references/memory.md +14 -0
  41. package/templates/editor/.github/skills/zen-cli/references/run.md +75 -16
  42. package/templates/editor/.github/skills/zen-memory/SKILL.md +52 -4
  43. package/templates/editor/.github/skills/zen-review/SKILL.md +2 -2
  44. package/templates/editor/.github/skills/zen-review/scripts/review.sh +1 -1
  45. package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +2 -2
  46. package/templates/editor/.github/skills/zen-spec-sync/scripts/snapshot.sh +1 -1
  47. package/templates/project/SPECIFICATION.md.tmpl +25 -2
  48. package/templates/project/agents.yaml.tmpl +3 -3
  49. package/templates/project/env +7 -1
  50. package/templates/project/gitignore +4 -4
@@ -29,6 +29,7 @@ skill.
29
29
  | Flag | Meaning |
30
30
  | ----------------------- | -------------------------------------------- |
31
31
  | `--project <name\|dir>` | Which project. Inferred from the directory |
32
+ | `--dir <dir>` | Read this memory directory instead |
32
33
  | `--kind <name>` | Only this kind of node |
33
34
  | `--audience <name>` | Only nodes committed under this label |
34
35
  | `--files` | Only nodes that remember a file |
@@ -38,6 +39,19 @@ skill.
38
39
  | `--open` | Open the exported page |
39
40
  | `--yes` | Do not ask before removing |
40
41
 
42
+ ## A graph that is not the project's
43
+
44
+ `--dir` opens a memory directory as it stands, and skips project resolution
45
+ entirely: there need be no `agents.yaml` anywhere above it. That is what to use
46
+ for a graph a run was pointed at with `zen run --memory <dir>`, and for a copy
47
+ taken out of a running session - copy the directory, delete its `.lock`, and
48
+ read the copy while the run continues.
49
+
50
+ ```
51
+ zen run --memory .tmp/mem "…" remember into .tmp/mem for this run
52
+ zen memory --dir .tmp/mem stats then read that graph, from anywhere
53
+ ```
54
+
41
55
  ## It reads unmasked, on purpose
42
56
 
43
57
  Memory is masked per agent: a node's `audience` decides who can see it. Recall
@@ -9,24 +9,78 @@ answers once on stdout and exits.
9
9
 
10
10
  ## Options
11
11
 
12
- | Flag | What it does |
13
- | ----------------------- | ------------------------------------------------------ |
14
- | `--project <name\|dir>` | Which project. Inferred from the directory otherwise |
15
- | `--session <id>` | Continue a particular session |
16
- | `--new` | Start a fresh one |
17
- | `--workspace <dir>` | What the agent may read and write |
18
- | `--model <ref>` | Override the default model for this run |
19
- | `--image <ref>` | Override the container image commands run in |
20
- | `--read-only` | Withhold every tool that can write |
21
- | `--quiet` | The answer only; no narration |
22
- | `--plain` | One shot, even on a terminal |
23
- | `--theme <dark\|light>` | Force the palette. Detected otherwise; `$ZENERA_THEME` |
24
- | `--out <file>` | Write the answer to a file as well as to stdout |
25
- | `--yes` | Accept the questions it would otherwise ask |
12
+ | Flag | What it does |
13
+ | ----------------------- | -------------------------------------------------------- |
14
+ | `--project <name\|dir>` | Which project. Inferred from the directory otherwise |
15
+ | `--session <id>` | Continue a particular session |
16
+ | `--new` | Start a new session rather than continuing one |
17
+ | `--workspace <dir>` | What the agent may read and write |
18
+ | `--memory <dir>` | Where the agents remember into, instead of the project's |
19
+ | `--model <ref>` | Override the default model for this run |
20
+ | `--image <ref>` | Override the container image commands run in |
21
+ | `--read-only` | Withhold every tool that can write |
22
+ | `--plain` | Never open the TUI; a prompt is then required |
23
+ | `--theme <dark\|light>` | Palette for the TUI; `auto` detects. `$ZENERA_THEME` |
24
+ | `--out <file>` | Put the answer in this file instead of on stdout |
25
+ | `--yes` | Say yes to the workspace and install questions |
26
26
 
27
27
  Flags always beat the file: the repository states intent, the invocation
28
28
  overrides it.
29
29
 
30
+ ## Where the answer goes
31
+
32
+ The answer goes to stdout and the progress to stderr, so `zen run "…" >
33
+ answer.md` leaves a file with the answer in it and nothing else, watched or not.
34
+ `--out` is a destination rather than a copy: it takes the answer off stdout, so
35
+ the file is the only place it lands.
36
+
37
+ ```
38
+ zen run "what changed?" > out.md answer in the file, progress on screen
39
+ zen run "what changed?" > out.md 2>/dev/null ... and nothing on screen at all
40
+ zen run --out out.md "what changed?" the same file, stdout left empty
41
+ ```
42
+
43
+ There is no `--quiet`. Narration is on stderr already, so a redirect is enough;
44
+ `--json` silences it for machine use.
45
+
46
+ `--json` answers with the paths as well as the answer: the session and run
47
+ directories, every file the run left behind, and every tree the agent could see,
48
+ with the path it had on this machine beside the path it had inside the
49
+ container.
50
+
51
+ ```json
52
+ {
53
+ "session": { "id": "...", "dir": "<project>/sessions/<id>" },
54
+ "run": {
55
+ "id": "...",
56
+ "dir": "<project>/sessions/<id>/runs/<run-id>",
57
+ "input": ".../input.md",
58
+ "output": ".../output.md",
59
+ "state": ".../state.json",
60
+ "meta": ".../meta.json",
61
+ "report": ".../report.html"
62
+ },
63
+ "mounts": [
64
+ { "host": "...", "at": "/workspace", "readOnly": false },
65
+ { "host": "...", "at": "/assets", "readOnly": true },
66
+ { "host": "...", "at": "/skills/<name>", "readOnly": true }
67
+ ],
68
+ "agent": "...",
69
+ "stopReason": "final",
70
+ "durationMs": 0,
71
+ "usage": {},
72
+ "output": "the answer"
73
+ }
74
+ ```
75
+
76
+ `mounts` also lists the container's `$HOME` and, when keys are forwarded, the
77
+ credential files mounted under `/run/zenera/keys`.
78
+
79
+ `--plain` never opens the TUI. The TUI is the only thing that can ask for a
80
+ prompt, so with `--plain` the prompt has to be there already - in the argument
81
+ or on stdin - and `zen run --plain` with neither is an error instead of a
82
+ window. Say it in a script that must not meet one.
83
+
30
84
  ## Which word is the project
31
85
 
32
86
  The first positional is read as a project when it names one, and as the first
@@ -42,7 +96,7 @@ zen run --project why "why?" when the project is called "why"
42
96
  The prompt comes from the argument, or from stdin, or from the TUI:
43
97
 
44
98
  ```
45
- git diff | zen run --quiet "summarise this diff"
99
+ git diff | zen run "summarise this diff"
46
100
  ```
47
101
 
48
102
  ## What a prompt on the command line implies
@@ -56,10 +110,15 @@ there is someone to ask, still asks.
56
110
  There is no `resume`. A session continues itself, because its state is what it
57
111
  is; `--session <id>` picks which one.
58
112
 
113
+ `--new` only matters when there is no prompt. A prompt on the command line
114
+ already starts a new session, so `zen run --new "what changed?"` is the same as
115
+ `zen run "what changed?"`. Without a prompt the TUI asks which session to
116
+ continue, and `--new` is how you skip that question. It contradicts `--session`.
117
+
59
118
  ## The TUI
60
119
 
61
120
  Drawn only when there is a terminal on both stdin and stdout, no prompt,
62
- no `--plain`, no `--quiet` and no `--json`. It streams the answer, shows
121
+ no `--plain` and no `--json`. It streams the answer, shows
63
122
  reasoning as it arrives, and reports per-turn and per-session token usage in the
64
123
  footer.
65
124
 
@@ -197,8 +197,9 @@ below.
197
197
  └── .lock held while a run is writing; a dead pid's lock is stale
198
198
  ```
199
199
 
200
- It is project state, not source, and it is not committed. The directory is
201
- mounted **read-only** at `/memory` in the agent's namespace.
200
+ It is committed with the project - everything but `.lock`, which is runtime
201
+ state. The directory is mounted **read-only** at `/memory` in the agent's
202
+ namespace.
202
203
 
203
204
  ## Designing a memory strategy
204
205
 
@@ -363,6 +364,40 @@ lands at `<project>/memory/files/<id>.<ext>` and appears to the agent as:
363
364
  | Mount | `/memory`, **read-only** |
364
365
  | Extension | preserved, because it decides how the file runs and renders |
365
366
 
367
+ ### The agent has to be able to write the file first
368
+
369
+ `memory_commit` **copies** a file that already exists. It is not an upload
370
+ channel and it cannot create one. So everything too big for a node's `text` - a
371
+ script, a config, an assembled passage - depends on the agent having a tool that
372
+ puts bytes in the **workspace**, and an agent with none can only ever commit
373
+ text no matter how much the memory design leans on `file` nodes.
374
+
375
+ | The agent's `tools:` | Can it commit a `file`? |
376
+ | ---------------------------- | ----------------------------------------------------------------------- |
377
+ | `write_file` / `apply_patch` | Yes. Write it, then name the same path |
378
+ | `run_command` only | Yes, but only under `/workspace` - a heredoc into `/tmp` is unreachable |
379
+ | Neither | No. Everything worth keeping has to fit on one line of `text` |
380
+
381
+ The workspace is the only tree that round-trips, because it is the only
382
+ writable one. `/assets`, `/skills` and `/memory` resolve as well - they are
383
+ mounts the file tools know by name - but they are **read-only**, so nothing can
384
+ be put there to be remembered. Every other absolute path is real to
385
+ `run_command` and outside the workspace root, so a file written to `/tmp` comes
386
+ back as:
387
+
388
+ ```
389
+ error: outside the workspace: /tmp/report.py
390
+ ```
391
+
392
+ A bare error carrying none of the `hint` the other memory refusals do, because
393
+ containment is a workspace answer rather than a memory one.
394
+
395
+ This is the trap in a `tools:` list trimmed to `run_command` alone: the shell
396
+ can create the file, so `file` nodes look supported, but nothing in the agent's
397
+ context ever uses workspace vocabulary and the model has no reason to prefer
398
+ `/workspace` over `/tmp`. Either give it the file tools, or say in its prompt
399
+ that work it means to keep goes in `/workspace`.
400
+
366
401
  Reusing one is the point of keeping it:
367
402
 
368
403
  ```
@@ -552,9 +587,17 @@ so inspection is free and offline.
552
587
  | Why was that recalled? | `zen memory show <id>` |
553
588
  | That should never have been written down | `zen memory forget <id>` |
554
589
 
590
+ The directory is not fixed. `zen run --memory <dir>` sends one run's memory
591
+ somewhere else - a scratch graph for a trial, one per branch, or a shared one
592
+ outside the repository - and `zen memory --dir <dir>` reads any such directory
593
+ with no project around it. Together they are also how you read a graph while a
594
+ run holds its `.lock`: copy the directory, delete the copy's `.lock`, and point
595
+ `--dir` at the copy.
596
+
555
597
  `zen inspect` answers the other half: a run report shows the recollection block
556
598
  exactly as the model received it, which is how you tell "memory had nothing"
557
- apart from "memory had it and the model ignored it".
599
+ apart from "memory had it and the model ignored it". A run given `--memory`
600
+ wants `zen inspect --memory <dir>` to rebuild its report against the same graph.
558
601
 
559
602
  ## A worked configuration
560
603
 
@@ -597,6 +640,9 @@ agents:
597
640
  - [ ] Every agent that should learn has a `memory:` binding - the top-level
598
641
  block alone enables nothing.
599
642
  - [ ] `access: full` appears at most once.
643
+ - [ ] Any agent the design expects to commit `file` nodes can write to the
644
+ workspace - `write_file`/`apply_patch`, or `run_command` with `/workspace`
645
+ named in its prompt. Nothing else round-trips.
600
646
  - [ ] `sees`/`writes` exist because an agent would be **misled** by the other
601
647
  slice, not merely because it does not need it.
602
648
  - [ ] `autoRecall: false` on any agent whose user message is a payload.
@@ -612,7 +658,7 @@ agents:
612
658
  | The `memory_*` tools are missing | No `memory:` on the agent. They come from the binding, never from `tools:` |
613
659
  | `zen memory` says the project has none | Neither a `memory:` block nor an agent binding - nothing is opened and no directory is made |
614
660
  | Recall finds a memory only when reworded | No embedder. `zen memory stats` says `embedding none` |
615
- | Recall finds nothing after changing the embedder | Refused rather than mixed - a manifest records the model. Re-embed or change it back |
661
+ | Recall finds nothing after changing the embedder | Refused rather than mixed - a manifest records the model. Change it back, or start a new memory |
616
662
  | Vectors fewer than nodes | Some nodes were committed with no embedder; they are only reachable by term overlap |
617
663
  | The graph fills with restated requests | The commit rule is not in a prompt or skill. State the "would a later run redo this" test |
618
664
  | An agent greps `/memory` or reads `graph.json` | The house-rules block is missing from `agents/memory-instructions.md` - the system prompt never forbids it |
@@ -621,6 +667,8 @@ agents:
621
667
  | A wrong memory keeps coming back | It was edited instead of superseded, or superseded in the wrong direction - the **new** node is the source |
622
668
  | An agent cannot see a node you can | Its `audience` is not in that agent's `sees`. Invisible and missing are the same thing, on purpose |
623
669
  | "this agent cannot remember files" | The agent has no workspace, so a path cannot be resolved. Commit without `file` |
670
+ | `error: outside the workspace: /tmp/…` | The file is real to the shell and outside the only tree memory reads. Write it under `/workspace` |
671
+ | Long content arrives as `text`, never as a `file` | The agent has no tool that writes to the workspace, so it has no path to name |
624
672
  | A remembered file will not run | It is not self-contained - it referenced the workspace it was written in |
625
673
  | Recollections are large and unhelpful | `autoRecall.limit` is too high, or node texts describe answers rather than questions |
626
674
  | The model recalls a route the API no longer has | Nothing re-checks memory against a rebuilt index. Supersede, and require verification |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: zen-review
3
- description: Checking a project before committing - the four mechanical checks `/review-project` makes before any judgement (`zen check`, the §2.5 path sweep, the memory house-rules copy, the spec-sync record), what each one owns, what their exit codes mean, and what deliberately has no check because no tool can make it. Load before running `/review-project`, before saying a project is ready, and whenever a review finding needs verifying rather than restating.
3
+ description: Checking a project before committing - the four mechanical checks `/project-review` makes before any judgement (`zen check`, the §2.5 path sweep, the memory house-rules copy, the spec-sync record), what each one owns, what their exit codes mean, and what deliberately has no check because no tool can make it. Load before running `/project-review`, before saying a project is ready, and whenever a review finding needs verifying rather than restating.
4
4
  ---
5
5
 
6
6
  # Reviewing a project
@@ -104,7 +104,7 @@ Not because it would be hard, but because the answer would be wrong:
104
104
  - **Whether the newest spec-sync history entry is honest.** A pass can claim
105
105
  anything; only reading the diff says whether it did it.
106
106
 
107
- That list is `/review-project`, and §9 of the copilot instructions is the long
107
+ That list is `/project-review`, and §9 of the copilot instructions is the long
108
108
  form. Run the script first so the prompt is spent on this.
109
109
 
110
110
  ## Where these scripts live, and why edits to them vanish
@@ -7,7 +7,7 @@
7
7
  #
8
8
  # What it prints is findings. What it cannot see — whether an agent has one job,
9
9
  # whether a description is a condition, whether a fact belongs in a skill — is
10
- # the rest of /review-project, and the reason that prompt still exists.
10
+ # the rest of /project-review, and the reason that prompt still exists.
11
11
  #
12
12
  # Every step runs even when an earlier one fails: a review that stops at the
13
13
  # first problem is one somebody has to run four times. The exit code is 1 if
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: zen-spec-sync
3
- description: Running `/sync-with-spec` as an incremental pass - the committed `.spec-sync` baseline of what the last pass applied, how to scope a pass from the specification and feedback diffs and from what moved in the project itself, the four checks a narrow pass may never skip, the iteration history format, and why the baseline is written last. Load before every `/sync-with-spec`, including the first one on a project that has no baseline yet.
3
+ description: Running `/spec-sync-project` as an incremental pass - the committed `.spec-sync` baseline of what the last pass applied, how to scope a pass from the specification and feedback diffs and from what moved in the project itself, the four checks a narrow pass may never skip, the iteration history format, and why the baseline is written last. Load before every `/spec-sync-project`, including the first one on a project that has no baseline yet.
4
4
  ---
5
5
 
6
6
  # Syncing a project with its specification, one change at a time
@@ -84,7 +84,7 @@ editing anything, and report it in the chat as the pass's scope.
84
84
  2. **The `SPECIFICATION-FEEDBACK.md` diff.** The file is a questionnaire the
85
85
  human answers by ticking one answer's `[ ]` box or deleting the answers they
86
86
  do not want. A question that has moved under `✅ Answered` was folded into the
87
- specification by `/update-spec-from-feedback`, and its **Still to implement**
87
+ specification by `/spec-apply-feedback`, and its **Still to implement**
88
88
  line is the work. A question with a box marked `[x]`, or one answer left
89
89
  under it, and no `✅` entry was answered and never folded in - implement it,
90
90
  put its text into `SPECIFICATION.md`, and say so. A new question someone else
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  #
3
- # The committed record of what the last completed /sync-with-spec pass applied.
3
+ # The committed record of what the last completed /spec-sync-project pass applied.
4
4
  #
5
5
  # snapshot.sh status full | incremental | tampered, and what differs
6
6
  # snapshot.sh diff the difference the next pass has to work
@@ -13,6 +13,22 @@ something in it — and report what was done in enough detail to be checked.
13
13
  - One request at a time, in one workspace, with the person who asked it present.
14
14
  - No schedule, no queue, nothing that outlives the session it was asked in.
15
15
 
16
+ ## Models
17
+
18
+ - `{{modelRef}}` — one model for the whole project, used by every agent.
19
+ - Named once in `agents.yaml` so it can be swapped in one place. Nothing here
20
+ depends on which model it is; a project that later needs a cheaper model for
21
+ one job adds a second alias rather than scattering the choice.
22
+
23
+ ## Setup
24
+
25
+ - Run once, before any session: whatever has to exist before work can start —
26
+ an index built, a document fetched, a file generated.
27
+ - `scripts/_setup.sh` is the one command that does it, and `scripts/<name>.sh`
28
+ are the steps it runs in order. A step is safe to run twice and reports
29
+ `skipped` the second time.
30
+ - Currently: no steps. Nothing has to be prepared before a run.
31
+
16
32
  ## Agents
17
33
 
18
34
  - `default` — the entry point and the only agent; nobody to hand work to.
@@ -34,13 +50,20 @@ something in it — and report what was done in enough detail to be checked.
34
50
 
35
51
  ## Environment
36
52
 
37
- - Model: `{{modelRef}}`, one for the whole project.
38
53
  - Memory: `memory/`, kept with the project and committed except for its
39
54
  vectors, which are derived.
40
55
  - Sandbox: built from `sandbox/Dockerfile`, persists between runs, and can
41
56
  reach the network. What the work always needs belongs in the image, not in an
42
57
  instruction that reinstalls it on every run.
43
- - Setup: none. `scripts/_setup.sh` is where a step would go, and it has none.
58
+
59
+ ## Testing
60
+
61
+ - `zen check` passes: the project loads, and every model, tool and skill an
62
+ agent is given resolves. It says nothing about whether the prose is right.
63
+ - `scripts/_setup.sh` run twice in a row reports every step `skipped` the
64
+ second time.
65
+ - A run against a real request: the workspace holds the change described, and
66
+ the reply's account of it matches what is on disk.
44
67
 
45
68
  ## Done means
46
69
 
@@ -1,8 +1,8 @@
1
1
  # Who exists, and what they may reach for.
2
2
  #
3
- # This implements the Agents, Tools and boundaries, and Environment sections of
4
- # SPECIFICATION.md. Change what the system does there first, then bring this
5
- # file to match it — `/sync-with-spec` in your editor does exactly that.
3
+ # This implements the Models, Agents, Tools and boundaries, and Environment
4
+ # sections of SPECIFICATION.md. Change what the system does there first, then bring this
5
+ # file to match it — `/spec-sync-project` in your editor does exactly that.
6
6
  #
7
7
  version: 1
8
8
 
@@ -3,7 +3,8 @@
3
3
  # `zen run` reads this file before it loads the project, and forwards every
4
4
  # name in it into the sandbox — so a command the agent runs sees the same
5
5
  # variables the run does. Values are forwarded by name, never written onto a
6
- # command line, and `--no-keys` withholds the whole file.
6
+ # command line, and `--no-keys` withholds the whole file. `zen meta` reads it
7
+ # too, for the one variable below that is addressed to it.
7
8
  #
8
9
  # Precedence: what is already set in your shell wins, then this file, then the
9
10
  # keyring (`zen key add`). So this is the place for what belongs to *this*
@@ -20,3 +21,8 @@
20
21
  # OPENAI_API_KEY=
21
22
  # ACME_API_TOKEN=
22
23
  # ACME_BASE_URL=https://api.example.test
24
+
25
+ # Which model `zen meta` runs the meta agent on, for this project only. A zen
26
+ # ref — `vertex/gemini-3.8-flash`, `openai/gpt-5.6-sol` — not a provider's wire
27
+ # name. `zen meta model` shows where the answer came from.
28
+ # ZENERA_META_MODEL=
@@ -2,10 +2,10 @@
2
2
  # None of it is source.
3
3
  sessions/
4
4
 
5
- # Memory is worth keeping: `graph.json` and `files/` are what the agents
6
- # learned, and they diff. The vectors are not — they are derived from the
7
- # graph and can be rebuilt with `zen memory reembed`.
8
- memory/vectors.*
5
+ # Memory is worth keeping: `graph.json`, `files/` and the vectors are what the
6
+ # agents learned. The vectors are binary, but nothing rebuilds them from the
7
+ # graph, so a clone without them recalls by term overlap until every node is
8
+ # written again. Only the lock is runtime state.
9
9
  memory/.lock
10
10
 
11
11
  # Scratch, inside the workspace so the sandbox can reach it.