@zenera/cli 1.1.43 → 1.1.45

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 (48) hide show
  1. package/dist/commands/completion.d.ts +1 -1
  2. package/dist/commands/completion.js +4 -4
  3. package/dist/commands/meta.js +10 -10
  4. package/dist/completion.js +1 -1
  5. package/dist/meta/editor.js +43 -3
  6. package/dist/meta/finetune/command.js +2 -2
  7. package/dist/meta/finetune/improvements.js +1 -1
  8. package/dist/meta/prompts.d.ts +8 -3
  9. package/dist/meta/prompts.js +42 -19
  10. package/dist/usage.d.ts +1 -1
  11. package/dist/usage.js +1 -1
  12. package/package.json +4 -4
  13. package/templates/editor/.github/copilot-instructions.md +35 -22
  14. package/templates/editor/.github/{prompts/analyze.prompt.md → skills/analyze/SKILL.md} +6 -3
  15. package/templates/editor/.github/{prompts/dataset.prompt.md → skills/dataset/SKILL.md} +6 -3
  16. package/templates/editor/.github/{prompts/finetune.prompt.md → skills/finetune/SKILL.md} +7 -4
  17. package/templates/editor/.github/{prompts/new-agent.prompt.md → skills/new-agent/SKILL.md} +3 -1
  18. package/templates/editor/.github/{prompts/new-skill.prompt.md → skills/new-skill/SKILL.md} +3 -1
  19. package/templates/editor/.github/{prompts/project-review.prompt.md → skills/project-review/SKILL.md} +6 -0
  20. package/templates/editor/.github/{prompts/spec-apply-feedback.prompt.md → skills/spec-apply-feedback/SKILL.md} +2 -0
  21. package/templates/editor/.github/skills/spec-edit/SKILL.md +324 -0
  22. package/templates/editor/.github/{prompts/spec-sync-project.prompt.md → skills/spec-sync-project/SKILL.md} +41 -15
  23. package/templates/editor/.github/skills/zen-analyze-run/SKILL.md +1 -0
  24. package/templates/editor/.github/skills/zen-cli/SKILL.md +21 -20
  25. package/templates/editor/.github/skills/zen-cli/references/finetune.md +1 -1
  26. package/templates/editor/.github/skills/zen-cli/references/meta.md +2 -2
  27. package/templates/editor/.github/skills/zen-code-python/SKILL.md +1 -0
  28. package/templates/editor/.github/skills/zen-dataset/SKILL.md +1 -0
  29. package/templates/editor/.github/skills/zen-finetune/SKILL.md +2 -1
  30. package/templates/editor/.github/{prompts/finetune-apply.prompt.md → skills/zen-finetune-apply/SKILL.md} +7 -4
  31. package/templates/editor/.github/skills/zen-inspect/SKILL.md +1 -0
  32. package/templates/editor/.github/skills/zen-inspect-ask/SKILL.md +1 -0
  33. package/templates/editor/.github/skills/zen-instructions/SKILL.md +1 -0
  34. package/templates/editor/.github/skills/zen-memory/SKILL.md +1 -0
  35. package/templates/editor/.github/skills/zen-memory-warmup/SKILL.md +1 -0
  36. package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +1 -0
  37. package/templates/editor/.github/skills/zen-rag-schema/SKILL.md +1 -0
  38. package/templates/editor/.github/skills/zen-review/SKILL.md +34 -0
  39. package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +1 -1
  40. package/templates/editor/.github/skills/zen-review/scripts/check-receipt.mjs +95 -0
  41. package/templates/editor/.github/skills/zen-review/scripts/review.sh +27 -2
  42. package/templates/editor/.github/skills/zen-sandbox/SKILL.md +2 -1
  43. package/templates/editor/.github/skills/zen-sandbox-capacity/SKILL.md +2 -1
  44. package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +115 -20
  45. package/templates/editor/.github/skills/zen-spec-sync/scripts/snapshot.sh +83 -2
  46. package/templates/editor/.github/skills/zen-topology/SKILL.md +1 -0
  47. package/templates/project/agents/prompts/default.md.tmpl +3 -3
  48. package/templates/editor/.github/prompts/spec-edit.prompt.md +0 -250
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: zen-inspect
3
3
  description: 'Use when an LLM agent must diagnose a recorded Zenera run non-interactively: read `zen inspect graph`, open selected nodes, replay an `llm_call` with `zen inspect ask`, identify loops, failed tools, wrong prompts, missed skills, early hand-offs, failed branches, compaction, latency, or token waste. Requires explicit run, node, and question arguments so commands remain deterministic and never prompt.'
4
+ user-invocable: false
4
5
  ---
5
6
 
6
7
  # Diagnose a run non-interactively
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: zen-inspect-ask
3
3
  description: 'Use when formulating a question for `zen inspect ask` about an `llm_call` node of a recorded Zenera run - especially a "why did you NOT do X" question (why no `memory_search` first, why no `fork` for independent research branches, why no skill, why no test run). Covers choosing the node, ruling out missing tools before asking, wording the counterfactual so the answer quotes the instruction that blocked the better behaviour, and verifying that quote against the recorded request.'
4
+ user-invocable: false
4
5
  ---
5
6
 
6
7
  # Asking a run why it did not do the better thing
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: zen-instructions
3
3
  description: The house rules - `agents/instructions.md` and every `agents/<topic>-instructions.md` prepended to every agent's system prompt. How the set is discovered and ordered, the `requires:` frontmatter and its closed vocabulary (`files`, `files-write`, `fork`, `memory`, `memory-write`, `memory-forget`), which four documents are `zen`'s own and are replaced by `zen check --fix`, how a project writes its own customisable topic prompts beside them (`files-policy-instructions.md`, `memory-policy-instructions.md`, `fork-<topic>-instructions.md`, `tools-<topic>-instructions.md`) without losing the copy, what belongs in a house rule as against an agent prompt or a skill, and every `zen check` finding these files can produce. Load before creating, editing, reviewing or deleting any `agents/*instructions.md`, before adding a capability whose rules arrive as one, and whenever `rules.stale`, `rules.unreached`, `files.uninstructed`, `memory.uninstructed` or `fork.uninstructed` is reported.
4
+ user-invocable: false
4
5
  ---
5
6
 
6
7
  # House rules
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: zen-memory
3
3
  description: Agent memory in Zenera projects - the `memory:` block and per-agent `access`/`sees`/`writes`/`autoRecall`, the five `memory_*` tools (when to use `memory_grep` instead of `memory_search`), kinds, relations and how recall ranks, inspecting a graph with `zen memory`, designing a memory strategy, `agents/memory-instructions.md` vs `agents/memory-policy-instructions.md`, remembering files under `/memory`, and putting memory in front of a `zen rag` index.
4
+ user-invocable: false
4
5
  ---
5
6
 
6
7
  # Memory
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: zen-memory-warmup
3
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
+ user-invocable: false
4
5
  ---
5
6
 
6
7
  # Memory Warmup
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: zen-rag-docs
3
3
  description: What a document index is, how to build one from a folder of `.md` files with `zen rag docs index` (or `npx @zenera/cli`), and how to ask it something - searching by meaning and narrowing to a release, a heading or a table, listing and grepping it exactly instead of reaching for shell `grep`/`rg`, reading a section back verbatim, giving it to an agent as tools, and writing the project skill that a wired-in index requires. For an OpenAPI description instead of prose, see `zen-rag-schema`.
4
+ user-invocable: false
4
5
  ---
5
6
 
6
7
  # The document index
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: zen-rag-schema
3
3
  description: What a schema index is, how it finds the right call inside a large OpenAPI/Swagger document, and how to build and query one with `zen rag schema` (or `npx @zenera/cli`) - searching it by meaning, listing and grepping it exactly instead of reaching for shell `grep`/`rg`, tracing a field up to the operations that carry it, giving it to an agent as tools, and writing the project skill that a wired-in index requires. For a pile of markdown instead of an API description, see `zen-rag-docs`.
4
+ user-invocable: false
4
5
  ---
5
6
 
6
7
  # The schema index
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: zen-review
3
3
  description: Checking a project before committing - the three mechanical checks `/project-review` makes before any judgement (`zen check`, the §2.5 path sweep, the spec-sync record), what each one owns, what their exit codes mean, which findings hand off to `zen-instructions`, 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
+ user-invocable: false
4
5
  ---
5
6
 
6
7
  # Reviewing a project
@@ -40,6 +41,39 @@ nothing about the model id it is spent on.
40
41
  Nothing below matters while `zen check` fails. Report what it said and fix that
41
42
  first.
42
43
 
44
+ ## Reusing a check within the same pass
45
+
46
+ Instead of calling `zen check` separately and repeating it in the review, run:
47
+
48
+ ```sh
49
+ .github/skills/zen-review/scripts/review.sh check full --fix
50
+ .github/skills/zen-review/scripts/review.sh --skip-check
51
+ ```
52
+
53
+ `check` runs only the CLI check and saves a receipt in `.tmp/review-check.json`
54
+ on success. `--skip-check` verifies that receipt, then still runs the path sweep
55
+ and spec-sync checks. It fails explicitly for a missing, failed, stale or
56
+ insufficient check; it never silently treats a skipped check as success.
57
+
58
+ Receipts cover project file contents, permissions, additions and deletions, and the CLI
59
+ version. They exclude only root `.git/`, `.tmp/` and `.spec-sync/`, so writing
60
+ history does not invalidate a result. Symlinks and special files are unsupported:
61
+ run the ordinary review instead. Hashing includes assets and dependencies and
62
+ can itself be costly for large projects; the ordinary review remains available.
63
+
64
+ This is same-pass reuse, not a persistent health cache. Do not reuse after
65
+ changing credentials, environment variables, external files, ignored scratch
66
+ inputs or remote resources: those are not certified by the receipt. Run a fresh
67
+ check instead. Only use reuse for a self-contained project's stable inputs.
68
+
69
+ `check no-sandbox` and `--skip-check no-sandbox` are for the spec-sync skill's
70
+ eligible model-reference-only pass. They still probe models. `check fast` and
71
+ `--skip-check fast` omit both expensive probes and are for iteration only.
72
+ A full receipt covers either partial mode, but a partial receipt cannot stand
73
+ in for a full one. If a partial check repairs or otherwise changes files, no
74
+ reusable receipt is issued: run `check full`, because a repair may have changed
75
+ the sandbox pin. Finish setup and all edits before saving the final receipt.
76
+
43
77
  ## The script this skill ships
44
78
 
45
79
  ### `check-paths.sh` - the §2.5 sweep
@@ -38,7 +38,7 @@ NOISE='^(and/or|he/she|his/her|input/output|read/write|w/o|n/a|N/A|24/7|km/h)$|^
38
38
  # second half is indistinguishable from a real absolute path once it is alone.
39
39
  #
40
40
  # A meta agent command, `zen meta run /spec-edit`, goes the same way: its
41
- # `/<name>` is a stored prompt, not a file the running agent is told to open.
41
+ # `/<name>` is a command (a skill), not a file the running agent is told to open.
42
42
  strip_urls() {
43
43
  sed -E -e 's#[a-zA-Z][a-zA-Z0-9+.-]*://[^][ )"'"'"'`>]*##g' \
44
44
  -e 's#(zen meta run( [A-Za-z0-9_.-]+)?) /[A-Za-z0-9_-]+#\1#g' "$1"
@@ -0,0 +1,95 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { lstatSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
4
+ import { resolve } from 'node:path';
5
+
6
+ const [action, mode = 'full', fix] = process.argv.slice(2);
7
+ const modes = {
8
+ full: [],
9
+ 'no-sandbox': ['--no-sandbox'],
10
+ fast: ['--no-models', '--no-sandbox'],
11
+ };
12
+ const receipt = '.tmp/review-check.json';
13
+
14
+ function fingerprint() {
15
+ const hash = createHash('sha256');
16
+ function walk(dir) {
17
+ for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) =>
18
+ a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
19
+ )) {
20
+ if (dir === '.' && ['.git', '.tmp', '.spec-sync'].includes(entry.name)) continue;
21
+ const path = `${dir}/${entry.name}`;
22
+ hash.update(JSON.stringify([path, entry.isDirectory(), lstatSync(path).mode]));
23
+ if (entry.isDirectory()) walk(path);
24
+ else if (entry.isFile())
25
+ hash.update(createHash('sha256').update(readFileSync(path)).digest('hex'));
26
+ else throw new Error(`cannot reuse validation with a symlink or special file: ${path}`);
27
+ }
28
+ }
29
+ walk('.');
30
+ return hash.digest('hex');
31
+ }
32
+
33
+ function version() {
34
+ const result = spawnSync('zen', ['--version'], { encoding: 'utf8' });
35
+ if (result.error) throw result.error;
36
+ if (result.status !== 0) throw new Error('zen --version failed');
37
+ return result.stdout.trim();
38
+ }
39
+
40
+ try {
41
+ if (
42
+ !Object.hasOwn(modes, mode) ||
43
+ !['run', 'verify'].includes(action) ||
44
+ (fix !== undefined && (action !== 'run' || fix !== '--fix')) ||
45
+ process.argv.length > 5
46
+ ) {
47
+ throw new Error('usage: check-receipt.mjs run|verify full|no-sandbox|fast [--fix]');
48
+ }
49
+ if (action === 'run') {
50
+ mkdirSync('.tmp', { recursive: true });
51
+ // Invalidate the previous result before trying a new check.
52
+ writeFileSync(receipt, 'null\n');
53
+ const before = fingerprint();
54
+ const result = spawnSync('zen', ['check', ...modes[mode], ...(fix ? [fix] : [])], {
55
+ stdio: 'inherit',
56
+ });
57
+ if (result.error) throw result.error;
58
+ if (result.status !== 0) process.exit(result.status ?? 1);
59
+ const after = fingerprint();
60
+ if (mode !== 'full' && before !== after) {
61
+ throw new Error(
62
+ 'files changed during a partial check (possibly a sandbox pin repair); run check full before reusing validation',
63
+ );
64
+ }
65
+ writeFileSync(
66
+ receipt,
67
+ JSON.stringify({
68
+ root: resolve('.'),
69
+ mode,
70
+ version: version(),
71
+ fingerprint: after,
72
+ }) + '\n',
73
+ );
74
+ console.log(`check receipt: ${receipt}`);
75
+ } else {
76
+ const saved = JSON.parse(readFileSync(receipt, 'utf8'));
77
+ const covers =
78
+ saved &&
79
+ (saved.mode === 'full' ||
80
+ saved.mode === mode ||
81
+ (saved.mode === 'no-sandbox' && mode === 'fast'));
82
+ if (
83
+ !covers ||
84
+ saved.root !== resolve('.') ||
85
+ saved.version !== version() ||
86
+ saved.fingerprint !== fingerprint()
87
+ ) {
88
+ throw new Error('check receipt is stale or lacks required probes; run a fresh check');
89
+ }
90
+ console.log(`zen check: reusing successful ${saved.mode} check`);
91
+ }
92
+ } catch (error) {
93
+ console.error(`check receipt: ${error.message}`);
94
+ process.exitCode = 1;
95
+ }
@@ -4,6 +4,8 @@
4
4
  #
5
5
  # review.sh zen check, then paths and the spec-sync record
6
6
  # review.sh fast the same, with `zen check --no-models --no-sandbox`
7
+ # review.sh check [full|no-sandbox|fast] [--fix] check and save a receipt
8
+ # review.sh --skip-check [full|no-sandbox|fast] verify and reuse that receipt
7
9
  #
8
10
  # What it prints is findings. What it cannot see — whether an agent has one job,
9
11
  # whether a description is a condition, whether a fact belongs in a skill — is
@@ -20,15 +22,34 @@ cd "$(dirname "$0")/../../../.."
20
22
 
21
23
  SKILLS=.github/skills
22
24
  CHECK_FLAGS=""
25
+ REUSE=0
26
+ RECEIPT="$SKILLS/zen-review/scripts/check-receipt.mjs"
27
+
28
+ if [ "${1:-}" = check ]; then
29
+ shift
30
+ if [ "${1:-}" = --fix ]; then
31
+ set -- full "$@"
32
+ fi
33
+ exec node "$RECEIPT" run "$@"
34
+ fi
35
+ if [ "${1:-}" = --skip-check ]; then
36
+ REUSE=1
37
+ shift
38
+ fi
23
39
 
24
40
  case "${1:-full}" in
25
41
  full) ;;
42
+ no-sandbox) CHECK_FLAGS="--no-sandbox" ;;
26
43
  fast) CHECK_FLAGS="--no-models --no-sandbox" ;;
27
44
  *)
28
- echo "usage: review.sh [full|fast]" >&2
45
+ echo "usage: review.sh [check|--skip-check] [full|no-sandbox|fast] [--fix (check only)]" >&2
29
46
  exit 2
30
47
  ;;
31
48
  esac
49
+ if [ "$#" -gt 1 ]; then
50
+ echo "review.sh: unexpected arguments" >&2
51
+ exit 2
52
+ fi
32
53
 
33
54
  SUMMARY=""
34
55
  WORST=0
@@ -47,7 +68,11 @@ step() {
47
68
  }
48
69
 
49
70
  step 'zen check'
50
- if command -v zen >/dev/null 2>&1; then
71
+ if [ "$REUSE" -eq 1 ]; then
72
+ node "$RECEIPT" verify "${1:-full}"
73
+ rc=$?
74
+ verdict 'zen check' "receipt exit $rc" "$rc"
75
+ elif command -v zen >/dev/null 2>&1; then
51
76
  # Unquoted on purpose: empty means no flags rather than one empty argument.
52
77
  # shellcheck disable=SC2086
53
78
  zen check $CHECK_FLAGS
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: zen-sandbox
3
- description: How the container sandbox works, when and how to configure it in `agents.yaml` (`sandbox:` and `agents[].sandbox`), the four `sandbox:*` tools, mount layout (/workspace, $HOME, /tmp, /run/zenera/keys), lifecycle and persistence (`persist: true`), credential forwarding by name, resource ceilings (cpus, memory, pids), standard vs strict hardening profiles, path rules, and debugging container issues with `zen sandbox`. Refers to `zen-cli` (`.github/skills/zen-cli/references/sandbox.md`).
3
+ description: 'How the container sandbox works, when and how to configure it in `agents.yaml` (`sandbox:` and `agents[].sandbox`), the four `sandbox:*` tools, mount layout (/workspace, $HOME, /tmp, /run/zenera/keys), lifecycle and persistence (`persist: true`), credential forwarding by name, resource ceilings (cpus, memory, pids), standard vs strict hardening profiles, path rules, and debugging container issues with `zen sandbox`. Refers to `zen-cli` (`.github/skills/zen-cli/references/sandbox.md`).'
4
+ user-invocable: false
4
5
  ---
5
6
 
6
7
  # The Container Sandbox
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: zen-sandbox-capacity
3
- description: Whether the machine can actually run the batch you are about to start. The podman VM is the ceiling and it is sized once, at 2 GiB by default, whatever the host has - so N concurrent containers over a multi-gigabyte index thrash and are OOM-killed while the host sits idle. Covers the three layers (host, VM, container) and which one can add memory, the exit-code table (137 is always capacity, 124 is usually 137 in disguise, and raising `timeout:` converts one into the other and hides the fault), the memory budget and how to measure its terms instead of assuming them, separating `local` probes from `embed` ones so a starved index is never mistaken for a throttled provider, recording the machine spec so two rounds' token counts are comparable at all, and `scripts/preflight_sandbox.sh` as the gate that answers it in under a minute. Load before any `zen run batch`, before raising `--concurrency` above 1, on exit 137, on a `killed` message from the shell, on an exit 124 from a `zen rag` call, when a batch is slow or timed out, and before comparing tokens or wall-clock between two rounds. Not for a single interactive `zen run` - one container is not a capacity problem.
3
+ description: 'Whether the machine can run the batch you are about to start: the podman VM (2 GiB by default) is the ceiling, so concurrent containers over a large index are OOM-killed while the host sits idle. Covers the host/VM/container layers, the exit codes (137 is capacity, 124 is usually 137 in disguise), measuring the memory budget, separating `local` from `embed` probes, recording the machine spec so rounds compare, and `scripts/preflight_sandbox.sh` as the gate. Load before any `zen run batch`, before `--concurrency` above 1, on exit 137 or 124 from `zen rag`, on a `killed` message, when a batch is slow or timed out, and before comparing tokens or wall-clock between rounds. Not for a single interactive `zen run`.'
4
+ user-invocable: false
4
5
  ---
5
6
 
6
7
  # Capacity
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: zen-spec-sync
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, which input wins when they disagree, the five checks a narrow pass may never skip, which skills a pass must load before editing (`zen-instructions` for any house rule it touches), 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.
3
+ description: Running `/spec-sync-project` incrementally - the committed `.spec-sync` baseline, scope and precedence, spec-only and model-reference-only verification tiers, affected capability skills, generated-rule maintenance, reusable validation, explicit history recording and why the baseline moves last. Load before every `/spec-sync-project`, including a project's first pass.
4
+ user-invocable: false
4
5
  ---
5
6
 
6
7
  # Syncing a project with its specification, one change at a time
@@ -13,7 +14,8 @@ one already made, and re-raises questions someone has already answered.
13
14
  A pass is worth repeating cheaply, so it keeps a record of **what it applied**.
14
15
  The next pass diffs the specification against that record and works the
15
16
  difference. What it must not do is confuse a narrow diff with a narrow
16
- obligation: the record scopes what you _read and edit_, never what you _verify_.
17
+ obligation: the record scopes what you _read and edit_. The tiers below also
18
+ scope verification, with explicit escalation when the work reaches further.
17
19
 
18
20
  ## What this owns
19
21
 
@@ -59,6 +61,14 @@ It prints one `mode:` line, the `applied:` stamp the baseline belongs to, and a
59
61
  `changed:` line per file that differs - specification, feedback, `agents.yaml`
60
62
  or anything under `agents/`:
61
63
 
64
+ The four generated `agents/{files,fork,memory,tools}-instructions.md` copies
65
+ print as `maintenance:` rather than `changed:`. Their diffs remain visible and
66
+ their bytes remain in the baseline. They are not project implementation edits:
67
+ `zen check` checks them against this version's templates, and `--fix` replaces
68
+ them. Account for repairs in history, but do not widen the pass merely because
69
+ one of these copies moved. A project requirement found in one must be moved to
70
+ a project-owned topic file; that is substantive work and widens the pass.
71
+
62
72
  | Mode | Means | Do |
63
73
  | ------------- | ------------------------------------------ | ------------------------------------------------------------------------------------- |
64
74
  | `full` | no manifest - no pass has ever completed | the whole prompt as written, then write the first baseline |
@@ -94,7 +104,7 @@ editing anything, and report it in the chat as the pass's scope.
94
104
  changed since the baseline is **not therefore implemented**: the baseline
95
105
  records what the pass applied, and a pass can complete with items open. This
96
106
  is the only place that says which.
97
- 4. **The implementation diff.** `agents.yaml` and every file under `agents/` -
107
+ 4. **The implementation diff.** `agents.yaml` and project-owned files under `agents/` -
98
108
  instructions, prompts and skills. This side is what was built rather than
99
109
  what was asked for, so it never narrows a pass: it says what moved under you
100
110
  since the last one. An agent, tool grant, handoff, model, house rule, prompt
@@ -158,12 +168,68 @@ answers:
158
168
  That is the whole of the work. A version mismatch is not a reason to look for
159
169
  anything else.
160
170
 
161
- ## The five checks a narrow pass may never skip
171
+ ## Choose a verification tier before loading capability skills
172
+
173
+ Use `full` for a missing or untrusted baseline. On an incremental pass, first
174
+ read the diffs and the applied entry's `Still open`, then choose `spec-only`,
175
+ `model-reference-only`, or the normal incremental checks below. Unresolved
176
+ items or feedback requiring other work disqualify a narrow tier. Say the tier,
177
+ its scope and the omitted work in the chat. Escalate as soon as the actual work
178
+ exceeds that scope; a small original diff is not permission to skip new work.
179
+
180
+ ### The model-reference-only pass
181
+
182
+ All of these must hold:
162
183
 
163
- However small the diff - with the one narrowing for a spec-only pass below:
184
+ - The only substantive requirement change replaces an existing **chat model
185
+ reference**, with an explicit replacement in the specification or user input.
186
+ - The only project implementation change is that corresponding scalar in
187
+ `agents.yaml`, either pending or already edited. No agent assignment, provider
188
+ configuration, tool, prompt, skill, capability or setup step changes.
189
+ - There is no other outstanding work from feedback or `Still open`.
164
190
 
165
- 1. **`zen check` passes.** Run it before the first edit and again after the
166
- last.
191
+ Embedding references are not eligible: indexes may need rebuilding. Version
192
+ pins are not model references; a pin repair requires sandbox validation.
193
+
194
+ Read only the affected requirement hunks, wiring and their direct references.
195
+ Load no capability skill for a model swap alone. After editing, run:
196
+
197
+ ```sh
198
+ .github/skills/zen-review/scripts/review.sh check no-sandbox
199
+ scripts/_setup.sh --check
200
+ .github/skills/zen-review/scripts/review.sh --skip-check no-sandbox
201
+ ```
202
+
203
+ The check still validates the whole configuration and probes its models; do not
204
+ also run `zen models test`. Every tier must resolve `rules.stale` and sandbox
205
+ pin findings before completion, even when the CLI reports them as warnings.
206
+ `--no-sandbox` is allowed only after confirming the
207
+ sandbox configuration, Dockerfile and build context are unchanged. Compare
208
+ them with git, inspect pending edits and account for any setup-generated build
209
+ inputs: the spec-sync baseline does not track those paths. If that cannot be
210
+ established, use `check full`. Use maintenance diffs and, when needed, a cheap
211
+ `zen check --no-models --no-sandbox` diagnostic to identify repairs before
212
+ probing models. If rules or pins need repair, load
213
+ `zen-instructions` only if a project-owned house rule needs editing, then run
214
+ `review.sh check full --fix`. It repairs before validating, including the
215
+ sandbox. No separate pre-repair model probe is required.
216
+
217
+ Work only the §9 **Models** and **Specification** groups, plus any group reached
218
+ by additional work (which may require escalation). If setup is empty, one
219
+ successful `--check` is all the setup proof. If existing steps all report
220
+ `skipped`, that also suffices for this tier; otherwise run setup, prove
221
+ re-entrancy and repeat the final check. Finish all builds before recording.
222
+
223
+ ### The normal incremental checks
224
+
225
+ Outside the two narrow tiers:
226
+
227
+ 1. **`zen check` passes after the last edit and setup.** Use
228
+ `review.sh check full --fix` to repair generated copies and pins before
229
+ validating. When an initial diagnostic is needed, use
230
+ `zen check --no-models --no-sandbox`; do not repeat expensive probes before
231
+ and after editing. Reuse the final receipt with `review.sh --skip-check`
232
+ rather than invoking a third check during review.
167
233
  2. **`scripts/_setup.sh` has run to completion in this session, and re-runs
168
234
  clean.** A one-line specification change can still break a setup step, and a
169
235
  pass that did not run it does not know. Then `scripts/_setup.sh --check` must
@@ -171,15 +237,16 @@ However small the diff - with the one narrowing for a spec-only pass below:
171
237
  doing the work, which is what tells a real `skipped` from a step that only
172
238
  looked done. A step that tests for a committed `manifest.json` skips on a
173
239
  fresh clone whose git-ignored `lance/` vectors are missing, and the project
174
- cannot search.
240
+ cannot search. When `STEPS` is empty, one successful `--check` is sufficient;
241
+ there is no execution or re-entrancy work to prove.
175
242
  3. **§9 of the copilot instructions, worked as a checklist**, group by group.
176
- 4. **The capability obligations in §0.1.** For every capability the project has
177
- turned on, load that capability's skill and re-check what it requires - in
178
- particular that `zen check` reports no `rules.stale`: a newer `zen` ships
179
- newer house rules, so they drift without anybody editing the project.
180
- 5. **`zen-instructions` is loaded before any house rule is touched.** A pass
243
+ 4. **The capability obligations in §0.1.** Load a capability skill only when
244
+ changed requirements, files, keys or unresolved work reach that capability.
245
+ For a full pass, every enabled capability is in scope. `zen check` must
246
+ still report no `rules.stale`, whether or not its capability was in scope.
247
+ 5. **`zen-instructions` is loaded before editing a project-owned house rule.** A pass
181
248
  whose scope reaches `agents/*instructions.md` - and input 4 puts it there
182
- whenever one of them moved - has to know that `tools-instructions.md`,
249
+ whenever a project-owned one moved - has to know that `tools-instructions.md`,
183
250
  `files-instructions.md`, `memory-instructions.md` and `fork-instructions.md`
184
251
  are `zen`'s and are replaced by `zen check --fix`. A pass that writes a
185
252
  requirement into one of them has implemented nothing: the next `open` reverts
@@ -188,7 +255,8 @@ However small the diff - with the one narrowing for a spec-only pass below:
188
255
 
189
256
  ### The spec-only pass
190
257
 
191
- A pass qualifies when **all** of the following hold:
258
+ A pass qualifies when **all** of the following hold, and no outstanding
259
+ feedback or `Still open` item requires implementation work:
192
260
 
193
261
  - the implementation diff (input 4) is empty
194
262
  - the pass edits nothing except `SPECIFICATION.md`, `SPECIFICATION-FEEDBACK.md`
@@ -196,10 +264,14 @@ A pass qualifies when **all** of the following hold:
196
264
  - no changed specification hunk names an agent, tool, model, embedding, skill,
197
265
  house rule, handoff, memory, index, asset or setup step
198
266
 
267
+ Generated copies and the editor tree repaired by `--fix` are maintenance, not
268
+ project implementation edits for this test.
269
+
199
270
  A pass like that changes nothing an agent reads, so it has no capability
200
271
  obligation to re-check. Checks 4 and 5 drop out: load no capability skill and no
201
- `zen-instructions`. `zen check` still runs before and after, and it still
202
- reports `rules.stale`. Check 2 shrinks to `scripts/_setup.sh --check`, which
272
+ `zen-instructions`. One final `review.sh check full --fix` repairs generated
273
+ copies and pins, validates the result and saves a reusable receipt. Check 2
274
+ shrinks to `scripts/_setup.sh --check`, which
203
275
  must report every step `skipped`. If it reports anything else, run
204
276
  `scripts/_setup.sh` in full as written. Check 3 shrinks to the §9
205
277
  **Specification** group and the groups covering any file the pass edited.
@@ -208,15 +280,18 @@ Decide this before loading anything, and say it in the chat - "spec-only pass:"
208
280
  followed by the reason. If any edit turns out to reach further, the pass is no
209
281
  longer spec-only: run all five checks.
210
282
 
211
- A narrow diff narrows reading and editing. It does not narrow verification.
283
+ Verification is narrowed only by these explicit tier rules.
212
284
 
213
285
  ## What else an incremental pass still owes
214
286
 
215
287
  The reverse pass of §2 - reading each line and naming the specification item it
216
- serves - runs over every file this pass touched, plus every file the
288
+ serves - runs over every project-owned file this pass touched, plus every file the
217
289
  implementation diff named. It is the only pass that finds a line contradicting
218
290
  another file, and an edit made this session, or made by hand since the last
219
291
  pass, is exactly where a new contradiction comes from.
292
+ Do not audit generated house-rule copies or the generated editor tree line by
293
+ line; byte comparison and repair belong to `zen check`. Inspect maintenance
294
+ diffs only to identify any project policy that must be preserved in an owned file.
220
295
 
221
296
  ## The history entry
222
297
 
@@ -226,6 +301,26 @@ there; a pass that did nothing still writes one saying so.
226
301
 
227
302
  These headings, in this order, because the next pass reads them:
228
303
 
304
+ Prefer `snapshot.sh record` over writing boilerplate by hand. Every section
305
+ must be supplied explicitly; repeat section flags for multiple items, and use
306
+ `None` for an empty section. Review the applied entry's unresolved items and
307
+ carry forward each one not closed; the script never silently copies or closes
308
+ them.
309
+
310
+ ```sh
311
+ .github/skills/zen-spec-sync/scripts/snapshot.sh record \
312
+ --mode incremental \
313
+ --scope "model-reference-only: replace the main chat model" \
314
+ --changed "agents.yaml: model reference now matches the specification" \
315
+ --feedback "None" \
316
+ --still-open "None" \
317
+ --checks "configuration and models pass; setup check passes; Models and Specification reviewed"
318
+ ```
319
+
320
+ This fills in the UTC stamp and specification hash, refuses to overwrite an
321
+ entry and leaves the baseline unchanged. It does not run checks or certify the
322
+ claims supplied to it. Run `commit` separately, only after verification.
323
+
229
324
  ```markdown
230
325
  # Sync pass 2026-09-13T104500Z
231
326
 
@@ -264,7 +359,7 @@ The one rule that must not be got wrong.
264
359
  .github/skills/zen-spec-sync/scripts/snapshot.sh commit
265
360
  ```
266
361
 
267
- runs **after** `zen check` passes, **after** `scripts/_setup.sh` has completed,
362
+ runs **after** `zen check` passes, **after** setup satisfies the selected tier,
268
363
  and **after** the history file is on disk. It takes the newest entry in
269
364
  `.spec-sync/history/` - the stamps sort chronologically by name - records it in
270
365
  `applied.txt`, and writes `manifest.txt` last of all. It refuses to run when
@@ -4,6 +4,7 @@
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
7
+ # snapshot.sh record write a history entry, without moving the baseline
7
8
  # snapshot.sh commit move the baseline on, once the pass has finished
8
9
  #
9
10
  # Run it from anywhere; it finds the project root from its own location, which
@@ -73,6 +74,13 @@ differs() {
73
74
  ! cmp -s "$(side "$BASE/$1")" "$(side "$1")"
74
75
  }
75
76
 
77
+ owned() {
78
+ case "$1" in
79
+ agents/files-instructions.md|agents/fork-instructions.md|agents/memory-instructions.md|agents/tools-instructions.md) return 0 ;;
80
+ *) return 1 ;;
81
+ esac
82
+ }
83
+
76
84
  # Which baseline files no longer hash to what the manifest says they do.
77
85
  # `FAILED` covers a changed file; `FAILED open or read` covers a missing one.
78
86
  broken() {
@@ -109,7 +117,11 @@ EOF
109
117
  fi
110
118
  while IFS= read -r file; do
111
119
  if [ -n "$file" ] && differs "$file"; then
112
- echo "changed: $file"
120
+ if owned "$file"; then
121
+ echo "maintenance: $file"
122
+ else
123
+ echo "changed: $file"
124
+ fi
113
125
  fi
114
126
  done <<EOF
115
127
  $(scope)
@@ -127,6 +139,74 @@ $(scope)
127
139
  EOF
128
140
  }
129
141
 
142
+ record() {
143
+ mode=""
144
+ scope_text=""
145
+ changed_text=""
146
+ feedback_text=""
147
+ open_text=""
148
+ checks_text=""
149
+ while [ "$#" -gt 0 ]; do
150
+ if [ "$#" -lt 2 ] || [ -z "$2" ]; then
151
+ echo "snapshot.sh record: every option needs a non-empty value" >&2
152
+ return 2
153
+ fi
154
+ case "$1" in
155
+ --mode)
156
+ case "$2" in
157
+ full|incremental) mode="$2" ;;
158
+ *) echo "snapshot.sh record: mode must be full or incremental" >&2; return 2 ;;
159
+ esac
160
+ ;;
161
+ --scope) scope_text="$scope_text- $2
162
+ " ;;
163
+ --changed) changed_text="$changed_text- $2
164
+ " ;;
165
+ --feedback) feedback_text="$feedback_text- $2
166
+ " ;;
167
+ --still-open) open_text="$open_text- $2
168
+ " ;;
169
+ --checks) checks_text="$checks_text- $2
170
+ " ;;
171
+ *) echo "snapshot.sh record: unknown option $1" >&2; return 2 ;;
172
+ esac
173
+ shift 2
174
+ done
175
+ if [ -z "$mode" ] || [ -z "$scope_text" ] || [ -z "$changed_text" ] ||
176
+ [ -z "$feedback_text" ] || [ -z "$open_text" ] || [ -z "$checks_text" ]; then
177
+ echo "usage: snapshot.sh record --mode full|incremental --scope <text> --changed <text> --feedback <text> --still-open <text> --checks <text> (repeat sections as needed; use 'None' explicitly)" >&2
178
+ return 2
179
+ fi
180
+ if [ ! -f SPECIFICATION.md ]; then
181
+ echo "snapshot.sh record: SPECIFICATION.md is missing" >&2
182
+ return 1
183
+ fi
184
+ spec_sum="$(shasum -a 256 SPECIFICATION.md)"
185
+ spec_sha="${spec_sum%% *}"
186
+ stamp="$(date -u +%Y-%m-%dT%H%M%SZ)"
187
+ entry="$HISTORY/$stamp.md"
188
+ draft="$(mktemp "$HISTORY/.record.XXXXXX")"
189
+ trap 'rm -f "$draft"' EXIT
190
+ {
191
+ printf '# Sync pass %s\n\n' "$stamp"
192
+ printf -- '- **Mode** - %s\n- **Spec sha** - %s\n' "$mode" "$spec_sha"
193
+ printf '\n## Scope\n\n%s' "$scope_text"
194
+ printf '\n## Changed\n\n%s' "$changed_text"
195
+ printf '\n## Feedback\n\n%s' "$feedback_text"
196
+ printf '\n## Still open\n\n%s' "$open_text"
197
+ printf '\n## Checks\n\n%s' "$checks_text"
198
+ } >"$draft"
199
+ # Linking refuses to replace an existing entry, even within the same second.
200
+ if ! ln "$draft" "$entry"; then
201
+ echo "snapshot.sh record: could not create $entry; no entry was overwritten" >&2
202
+ return 1
203
+ fi
204
+ rm -f "$draft"
205
+ trap - EXIT
206
+ echo "history: $entry"
207
+ echo "baseline: unchanged (run snapshot.sh commit only after verification)"
208
+ }
209
+
130
210
  commit() {
131
211
  entry="$(newest)"
132
212
  if [ -z "$entry" ]; then
@@ -192,9 +272,10 @@ EOF
192
272
  case "${1:-status}" in
193
273
  status) status ;;
194
274
  diff) show_diff ;;
275
+ record) shift; record "$@" ;;
195
276
  commit) commit ;;
196
277
  *)
197
- echo "usage: snapshot.sh [status|diff|commit]" >&2
278
+ echo "usage: snapshot.sh [status|diff|record|commit]" >&2
198
279
  exit 2
199
280
  ;;
200
281
  esac