@zenera/cli 1.1.16 → 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.
- package/README.md +110 -19
- package/dist/banner.d.ts +19 -0
- package/dist/banner.js +12 -9
- package/dist/catalog.js +19 -20
- package/dist/command.d.ts +3 -0
- package/dist/commands/check.js +5 -1
- package/dist/commands/index.js +4 -1
- package/dist/commands/init.js +42 -7
- package/dist/commands/inspect.js +77 -13
- package/dist/commands/key.js +49 -1
- package/dist/commands/memory.js +16 -3
- package/dist/commands/meta.d.ts +3 -0
- package/dist/commands/meta.js +596 -0
- package/dist/commands/run.js +64 -35
- package/dist/engine.d.ts +15 -0
- package/dist/engine.js +26 -1
- package/dist/env.d.ts +20 -0
- package/dist/env.js +65 -0
- package/dist/home.d.ts +2 -0
- package/dist/home.js +2 -0
- package/dist/keys.d.ts +2 -0
- package/dist/keys.js +2 -0
- package/dist/main.js +1 -1
- package/dist/meta.d.ts +221 -0
- package/dist/meta.js +695 -0
- package/dist/podman.js +64 -2
- package/dist/projects.d.ts +9 -1
- package/dist/projects.js +19 -2
- package/dist/resolve.js +7 -1
- package/dist/sandbox.d.ts +11 -0
- package/dist/sandbox.js +24 -4
- package/dist/scaffold.d.ts +6 -0
- package/dist/scaffold.js +56 -15
- package/dist/session.d.ts +10 -0
- package/dist/session.js +19 -1
- package/dist/term.d.ts +7 -0
- package/dist/term.js +8 -1
- package/dist/tui/app.js +33 -5
- package/dist/tui/wrap.d.ts +1 -1
- package/dist/tui/wrap.js +1 -1
- package/dist/validate.js +57 -15
- package/package.json +3 -2
- package/templates/editor/.github/copilot-instructions.md +313 -94
- package/templates/editor/.github/prompts/project-review.prompt.md +74 -0
- package/templates/editor/.github/prompts/spec-apply-feedback.prompt.md +142 -0
- package/templates/editor/.github/prompts/spec-sync-project.prompt.md +392 -0
- package/templates/editor/.github/skills/zen-cli/references/check.md +6 -3
- package/templates/editor/.github/skills/zen-cli/references/frame.md +5 -3
- package/templates/editor/.github/skills/zen-cli/references/inspect.md +4 -3
- package/templates/editor/.github/skills/zen-cli/references/memory.md +14 -0
- package/templates/editor/.github/skills/zen-cli/references/projects.md +19 -2
- package/templates/editor/.github/skills/zen-cli/references/run.md +75 -16
- package/templates/editor/.github/skills/zen-cli/references/sandbox.md +6 -0
- package/templates/editor/.github/skills/zen-memory/SKILL.md +153 -33
- package/templates/editor/.github/skills/zen-memory/scripts/check-instructions.sh +115 -0
- package/templates/editor/.github/skills/zen-review/SKILL.md +123 -0
- package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +119 -0
- package/templates/editor/.github/skills/zen-review/scripts/review.sh +104 -0
- package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +197 -0
- package/templates/editor/.github/skills/zen-spec-sync/scripts/snapshot.sh +200 -0
- package/templates/editor/.vscode/settings.json +44 -4
- package/templates/parts/memory-terms.yaml.tmpl +16 -0
- package/templates/parts/memory.yaml.tmpl +12 -0
- package/templates/project/SPECIFICATION.md.tmpl +32 -2
- package/templates/project/agents/memory-instructions.md +278 -0
- package/templates/project/agents.yaml.tmpl +12 -3
- package/templates/project/env +28 -0
- package/templates/project/gitignore +4 -4
- package/templates/editor/.github/prompts/review-project.prompt.md +0 -36
- package/templates/editor/.github/prompts/sync-with-spec.prompt.md +0 -205
- /package/templates/project/{INSTRUCTIONS.md → agents/instructions.md} +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zen-review
|
|
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
|
+
---
|
|
5
|
+
|
|
6
|
+
# Reviewing a project
|
|
7
|
+
|
|
8
|
+
A review of an agent project is two jobs wearing one name. One half is
|
|
9
|
+
decidable - a schema is valid or it is not, a path resolves or it does not, a
|
|
10
|
+
copy matches its reference or it does not - and a human doing it by hand gets it
|
|
11
|
+
wrong in the same three places every time. The other half is judgement: whether
|
|
12
|
+
an agent has one job, whether a description routes, whether a number is in the
|
|
13
|
+
wrong file.
|
|
14
|
+
|
|
15
|
+
This skill owns the first half, so the prompt can be all of the second.
|
|
16
|
+
|
|
17
|
+
## The one command
|
|
18
|
+
|
|
19
|
+
From the project root, or anywhere at all:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
.github/skills/zen-review/scripts/review.sh
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
It runs four checks in the order a reviewer needs them and prints a summary:
|
|
26
|
+
|
|
27
|
+
| Step | Runs | Owns |
|
|
28
|
+
| ----------- | ------------------------------------------ | -------------------------------------------- |
|
|
29
|
+
| `zen check` | the CLI | everything decidable about the configuration |
|
|
30
|
+
| `paths` | `zen-review/scripts/check-paths.sh` | paths a running agent cannot open - §2.5 |
|
|
31
|
+
| `memory` | `zen-memory/scripts/check-instructions.sh` | the house-rules copy, byte for byte - §5.3 |
|
|
32
|
+
| `spec-sync` | `zen-spec-sync/scripts/snapshot.sh status` | whether the record of the last pass is sound |
|
|
33
|
+
|
|
34
|
+
Every step runs even when an earlier one fails, because a review that stops at
|
|
35
|
+
the first problem is one somebody has to run four times. The exit code is `1` if
|
|
36
|
+
any step wants attention and `0` if none does. `review.sh fast` passes
|
|
37
|
+
`--no-models --no-sandbox` to `zen check`, which is the right call while
|
|
38
|
+
iterating and the wrong one before committing - a key that authenticates says
|
|
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
|
|
42
|
+
first.
|
|
43
|
+
|
|
44
|
+
## The two scripts this skill ships
|
|
45
|
+
|
|
46
|
+
### `check-paths.sh` - the §2.5 sweep
|
|
47
|
+
|
|
48
|
+
A running agent's whole world is four paths: `/workspace`, `/assets`, `/skills`
|
|
49
|
+
and `/memory`. Every path-shaped token in `agents/*instructions.md`,
|
|
50
|
+
`agents/prompts/*.md` and a skill's markdown is read by a model as an
|
|
51
|
+
instruction to open something, so anything outside those four is an instruction
|
|
52
|
+
to open a file that is not there.
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
.github/skills/zen-review/scripts/check-paths.sh # each candidate, with file and line
|
|
56
|
+
.github/skills/zen-review/scripts/check-paths.sh raw # the tokens alone, unfiltered
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
It exits `1` whenever it prints a candidate, because the list is meant to be
|
|
60
|
+
emptied or explained rather than skimmed. It strips urls before tokenising, skips
|
|
61
|
+
a handful of English constructions that contain a slash, and leaves
|
|
62
|
+
`agents/memory-instructions.md` out entirely - that file is worked examples by
|
|
63
|
+
design, and `check-instructions.sh` is the check it actually needs.
|
|
64
|
+
|
|
65
|
+
Everything it does print is either noise the script has not learned yet or a
|
|
66
|
+
real finding. There is no third case. A candidate that is genuinely noise is a
|
|
67
|
+
one-line addition to `NOISE` in the script - **upstream**, see below, not in the
|
|
68
|
+
copy sitting in this project.
|
|
69
|
+
|
|
70
|
+
`raw` is the sweep with nothing filtered, for when you suspect the filter.
|
|
71
|
+
|
|
72
|
+
### `check-instructions.sh` - the memory house rules
|
|
73
|
+
|
|
74
|
+
Lives in the `zen-memory` skill, because it is about memory and it is read at
|
|
75
|
+
the moment that skill is:
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
.github/skills/zen-memory/scripts/check-instructions.sh # the verdict
|
|
79
|
+
.github/skills/zen-memory/scripts/check-instructions.sh diff # and every differing line
|
|
80
|
+
.github/skills/zen-memory/scripts/check-instructions.sh fix # copy the reference over
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`agents/memory-instructions.md` must be a byte-identical copy of the `zen-memory`
|
|
84
|
+
skill's `references/memory-instructions.md`, and the reference is rewritten by
|
|
85
|
+
every `zen init` and `zen open`, so the copy goes stale without anyone touching
|
|
86
|
+
it. It drifts by **trailing whitespace inside tables**, which is invisible on
|
|
87
|
+
screen - this is the check no one should be asked to make by eye. When the
|
|
88
|
+
difference is whitespace alone the script says so, because that is the usual
|
|
89
|
+
cause and the least believable finding.
|
|
90
|
+
|
|
91
|
+
`fix` is the one-line `cp` and nothing else. Project policy belongs in
|
|
92
|
+
`agents/memory-policy-instructions.md`, which filename order puts directly after
|
|
93
|
+
the copy - see the `zen-memory` skill.
|
|
94
|
+
|
|
95
|
+
## What has no check, on purpose
|
|
96
|
+
|
|
97
|
+
Not because it would be hard, but because the answer would be wrong:
|
|
98
|
+
|
|
99
|
+
- **Whether an agent has one job.** Read the prompt.
|
|
100
|
+
- **Whether a description routes.** Two overlapping descriptions both parse.
|
|
101
|
+
- **Whether a fact belongs in a skill.** A threshold in a prompt is valid YAML.
|
|
102
|
+
- **Whether a tool grant is justified.** `zen check` confirms the selector
|
|
103
|
+
resolves, never that the agent needs it.
|
|
104
|
+
- **Whether the newest spec-sync history entry is honest.** A pass can claim
|
|
105
|
+
anything; only reading the diff says whether it did it.
|
|
106
|
+
|
|
107
|
+
That list is `/project-review`, and §9 of the copilot instructions is the long
|
|
108
|
+
form. Run the script first so the prompt is spent on this.
|
|
109
|
+
|
|
110
|
+
## Where these scripts live, and why edits to them vanish
|
|
111
|
+
|
|
112
|
+
`zen init` and `zen open` rewrite the whole `.github/` tree from the version of
|
|
113
|
+
`zen` in hand. These scripts arrive with it, so a change made to the copy in a
|
|
114
|
+
project is gone at the next `open`. A filter that should hold for every project
|
|
115
|
+
belongs in the distribution - `packages/cli/templates/editor/.github/skills/` in
|
|
116
|
+
the @zenera/neo repository - and reaches this project the next time the tree is
|
|
117
|
+
written.
|
|
118
|
+
|
|
119
|
+
Do not confuse these with a project's own scripts. `agents/skills/<name>/scripts/`
|
|
120
|
+
is run by **the project's agents**, inside the sandbox, at
|
|
121
|
+
`/skills/<name>/scripts/…`. `.github/skills/<name>/scripts/` is run by **the
|
|
122
|
+
editor's assistant**, on the host, at a workspace path. The two have different
|
|
123
|
+
readers and neither can see the other.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Every path-shaped token a running agent would be told to open, minus the ones
|
|
4
|
+
# that exist. The §2.5 sweep, as a script rather than a command to retype.
|
|
5
|
+
#
|
|
6
|
+
# check-paths.sh each candidate, with the file and line it is on
|
|
7
|
+
# check-paths.sh raw the tokens alone, unfiltered — the sweep itself
|
|
8
|
+
#
|
|
9
|
+
# A running agent sees four paths: /workspace, /assets, /skills and /memory.
|
|
10
|
+
# Everything else printed here is either noise this script has not learned yet
|
|
11
|
+
# or an instruction to open a file that is not there. There is no third case,
|
|
12
|
+
# which is why this exits non-zero whenever it prints one: the list is meant to
|
|
13
|
+
# be emptied or explained, not skimmed.
|
|
14
|
+
#
|
|
15
|
+
# Run it from anywhere; it finds the project root from its own location.
|
|
16
|
+
|
|
17
|
+
set -eu
|
|
18
|
+
cd "$(dirname "$0")/../../../.."
|
|
19
|
+
|
|
20
|
+
# What a path looks like in prose. §2.5 of the copilot instructions describes
|
|
21
|
+
# the sweep and sends you here for it, so this is the only copy of the pattern.
|
|
22
|
+
# Every segment after the first is taken too: matched one segment at a time,
|
|
23
|
+
# `/skills/house_style/examples.md` comes back as a mount that resolves *and* a
|
|
24
|
+
# stray `/examples.md`, and that fragment is noise on every run of every project.
|
|
25
|
+
TOKEN='[~.]?/?[A-Za-z0-9_.-]*(/[A-Za-z0-9_.*-]+)+'
|
|
26
|
+
|
|
27
|
+
# The four mounts, and only those: a token under one of them is a path that
|
|
28
|
+
# resolves.
|
|
29
|
+
MOUNTS='^/(workspace|assets|skills|memory)(/|$)'
|
|
30
|
+
|
|
31
|
+
# English that happens to contain a slash, and the placeholder paths of a
|
|
32
|
+
# worked example. Add to this rather than teaching a reviewer to skip the same
|
|
33
|
+
# line every run.
|
|
34
|
+
NOISE='^(and/or|he/she|his/her|input/output|read/write|w/o|n/a|N/A|24/7|km/h)$|^(path|foo)/'
|
|
35
|
+
|
|
36
|
+
# A url, stripped before tokenising rather than filtered after it — the token
|
|
37
|
+
# regex reads `https://acme.dev/docs` as `//acme.dev` *and* `/docs`, and the
|
|
38
|
+
# second half is indistinguishable from a real absolute path once it is alone.
|
|
39
|
+
strip_urls() {
|
|
40
|
+
sed -E 's#[a-zA-Z][a-zA-Z0-9+.-]*://[^][ )"'"'"'`>]*##g' "$1"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# The three trees §2.5 names, prose only: a skill's own script is code the
|
|
44
|
+
# sandbox runs, not text rendered into a prompt, and its paths are its own.
|
|
45
|
+
#
|
|
46
|
+
# `agents/memory-instructions.md` is left out on purpose. It is a copy of the
|
|
47
|
+
# reference, full of worked examples, so it fails this sweep by design and
|
|
48
|
+
# every run of it — and it has a check of its own that a sweep cannot make:
|
|
49
|
+
# .github/skills/zen-memory/scripts/check-instructions.sh
|
|
50
|
+
files() {
|
|
51
|
+
{
|
|
52
|
+
if [ -d agents/prompts ]; then find agents/prompts -type f -name '*.md'; fi
|
|
53
|
+
if [ -d agents/skills ]; then find agents/skills -type f -name '*.md'; fi
|
|
54
|
+
for file in agents/*instructions.md; do
|
|
55
|
+
if [ -f "$file" ]; then echo "$file"; fi
|
|
56
|
+
done
|
|
57
|
+
} | grep -v '^agents/memory-instructions\.md$' | sort -u
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Every token in those files, urls removed first, and a sentence's full stop
|
|
61
|
+
# trimmed off the end — no path ends in a dot, and `/workspace.` would
|
|
62
|
+
# otherwise read as a mount that does not exist.
|
|
63
|
+
sweep() {
|
|
64
|
+
while IFS= read -r file; do
|
|
65
|
+
if [ -n "$file" ]; then
|
|
66
|
+
strip_urls "$file"
|
|
67
|
+
fi
|
|
68
|
+
done <<EOF
|
|
69
|
+
$(files)
|
|
70
|
+
EOF
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
tokens() {
|
|
74
|
+
sweep | grep -Eoh "$TOKEN" | sed 's/\.*$//' | sort -u
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
candidates() {
|
|
78
|
+
tokens | grep -Ev "$MOUNTS" | grep -Ev "$NOISE" || true
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Where a token came from, so a finding names a file and a line rather than
|
|
82
|
+
# sending someone back to grep for it.
|
|
83
|
+
where() {
|
|
84
|
+
while IFS= read -r file; do
|
|
85
|
+
if [ -n "$file" ]; then
|
|
86
|
+
grep -nF -- "$1" "$file" | sed "s|^| $file:|" | cut -d: -f1,2
|
|
87
|
+
fi
|
|
88
|
+
done <<EOF
|
|
89
|
+
$(files)
|
|
90
|
+
EOF
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
report() {
|
|
94
|
+
found=0
|
|
95
|
+
while IFS= read -r token; do
|
|
96
|
+
if [ -n "$token" ]; then
|
|
97
|
+
found=$((found + 1))
|
|
98
|
+
echo "path: $token"
|
|
99
|
+
where "$token"
|
|
100
|
+
fi
|
|
101
|
+
done <<EOF
|
|
102
|
+
$(candidates)
|
|
103
|
+
EOF
|
|
104
|
+
if [ "$found" -eq 0 ]; then
|
|
105
|
+
echo "ok: every path in the three trees is under a mount"
|
|
106
|
+
return 0
|
|
107
|
+
fi
|
|
108
|
+
echo "candidates: $found to explain or fix"
|
|
109
|
+
return 1
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
case "${1:-report}" in
|
|
113
|
+
report) report ;;
|
|
114
|
+
raw) tokens ;;
|
|
115
|
+
*)
|
|
116
|
+
echo "usage: check-paths.sh [report|raw]" >&2
|
|
117
|
+
exit 2
|
|
118
|
+
;;
|
|
119
|
+
esac
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Every check a review can make without judgement, in the order it makes them.
|
|
4
|
+
#
|
|
5
|
+
# review.sh zen check, then paths, memory, and the spec-sync record
|
|
6
|
+
# review.sh fast the same, with `zen check --no-models --no-sandbox`
|
|
7
|
+
#
|
|
8
|
+
# What it prints is findings. What it cannot see — whether an agent has one job,
|
|
9
|
+
# whether a description is a condition, whether a fact belongs in a skill — is
|
|
10
|
+
# the rest of /project-review, and the reason that prompt still exists.
|
|
11
|
+
#
|
|
12
|
+
# Every step runs even when an earlier one fails: a review that stops at the
|
|
13
|
+
# first problem is one somebody has to run four times. The exit code is 1 if
|
|
14
|
+
# anything wants attention, 0 if nothing does.
|
|
15
|
+
#
|
|
16
|
+
# Run it from anywhere; it finds the project root from its own location.
|
|
17
|
+
|
|
18
|
+
set -u
|
|
19
|
+
cd "$(dirname "$0")/../../../.."
|
|
20
|
+
|
|
21
|
+
SKILLS=.github/skills
|
|
22
|
+
CHECK_FLAGS=""
|
|
23
|
+
|
|
24
|
+
case "${1:-full}" in
|
|
25
|
+
full) ;;
|
|
26
|
+
fast) CHECK_FLAGS="--no-models --no-sandbox" ;;
|
|
27
|
+
*)
|
|
28
|
+
echo "usage: review.sh [full|fast]" >&2
|
|
29
|
+
exit 2
|
|
30
|
+
;;
|
|
31
|
+
esac
|
|
32
|
+
|
|
33
|
+
SUMMARY=""
|
|
34
|
+
WORST=0
|
|
35
|
+
|
|
36
|
+
# Records one step's verdict for the summary, and remembers the worst of them.
|
|
37
|
+
verdict() {
|
|
38
|
+
SUMMARY="$SUMMARY$1: $2
|
|
39
|
+
"
|
|
40
|
+
if [ "$3" -ne 0 ]; then
|
|
41
|
+
WORST=1
|
|
42
|
+
fi
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
step() {
|
|
46
|
+
printf '\n--- %s ---\n' "$1"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
step 'zen check'
|
|
50
|
+
if command -v zen >/dev/null 2>&1; then
|
|
51
|
+
# Unquoted on purpose: empty means no flags rather than one empty argument.
|
|
52
|
+
# shellcheck disable=SC2086
|
|
53
|
+
zen check $CHECK_FLAGS
|
|
54
|
+
rc=$?
|
|
55
|
+
if [ "$rc" -eq 0 ]; then
|
|
56
|
+
verdict 'zen check' 'ok' 0
|
|
57
|
+
else
|
|
58
|
+
verdict 'zen check' "exit $rc — nothing below matters until this passes" 1
|
|
59
|
+
fi
|
|
60
|
+
else
|
|
61
|
+
echo "zen is not on PATH — install it, or run the other three alone"
|
|
62
|
+
verdict 'zen check' 'not run' 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
step 'paths'
|
|
66
|
+
"$SKILLS/zen-review/scripts/check-paths.sh"
|
|
67
|
+
rc=$?
|
|
68
|
+
if [ "$rc" -eq 0 ]; then
|
|
69
|
+
verdict 'paths' 'ok' 0
|
|
70
|
+
else
|
|
71
|
+
verdict 'paths' 'candidates to explain or fix' 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
step 'memory'
|
|
75
|
+
"$SKILLS/zen-memory/scripts/check-instructions.sh"
|
|
76
|
+
rc=$?
|
|
77
|
+
if [ "$rc" -eq 0 ]; then
|
|
78
|
+
verdict 'memory' 'ok' 0
|
|
79
|
+
else
|
|
80
|
+
verdict 'memory' 'the house rules are missing or stale' 1
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
step 'spec-sync'
|
|
84
|
+
if [ -f SPECIFICATION.md ]; then
|
|
85
|
+
# `status` reports a tampered baseline rather than failing on one, so the
|
|
86
|
+
# verdict has to read what it said.
|
|
87
|
+
state="$("$SKILLS/zen-spec-sync/scripts/snapshot.sh" status)"
|
|
88
|
+
rc=$?
|
|
89
|
+
printf '%s\n' "$state"
|
|
90
|
+
if [ "$rc" -ne 0 ]; then
|
|
91
|
+
verdict 'spec-sync' "exit $rc" 1
|
|
92
|
+
elif printf '%s' "$state" | grep -q 'mode: tampered'; then
|
|
93
|
+
verdict 'spec-sync' 'the baseline does not match its own manifest' 1
|
|
94
|
+
else
|
|
95
|
+
verdict 'spec-sync' "$(printf '%s' "$state" | sed -n 's/^mode: /mode /p')" 0
|
|
96
|
+
fi
|
|
97
|
+
else
|
|
98
|
+
echo "no SPECIFICATION.md — this project keeps no spec-sync record"
|
|
99
|
+
verdict 'spec-sync' 'not applicable' 0
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
step 'summary'
|
|
103
|
+
printf '%s' "$SUMMARY"
|
|
104
|
+
exit "$WORST"
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
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, 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
|
+
---
|
|
5
|
+
|
|
6
|
+
# Syncing a project with its specification, one change at a time
|
|
7
|
+
|
|
8
|
+
`SPECIFICATION.md` is the intent and the files are the implementation, so
|
|
9
|
+
bringing the two together is a pass over the whole project. Done from nothing
|
|
10
|
+
every time, that pass re-reads every prompt, re-derives every decision the last
|
|
11
|
+
one already made, and re-raises questions someone has already answered.
|
|
12
|
+
|
|
13
|
+
A pass is worth repeating cheaply, so it keeps a record of **what it applied**.
|
|
14
|
+
The next pass diffs the specification against that record and works the
|
|
15
|
+
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
|
+
|
|
18
|
+
## What this owns
|
|
19
|
+
|
|
20
|
+
Everything is committed. `.spec-sync/` is a record, not scratch - `.tmp/` is
|
|
21
|
+
scratch.
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
.spec-sync/
|
|
25
|
+
├── baseline/
|
|
26
|
+
│ ├── SPECIFICATION.md the spec the last completed pass applied
|
|
27
|
+
│ ├── SPECIFICATION-FEEDBACK.md the questions it had open, if any
|
|
28
|
+
│ ├── agents.yaml the wiring it left behind
|
|
29
|
+
│ ├── agents/ and every instruction, prompt and skill
|
|
30
|
+
│ ├── applied.txt the history entry this baseline belongs to
|
|
31
|
+
│ └── manifest.txt sha256 of every file above
|
|
32
|
+
└── history/
|
|
33
|
+
└── 2026-09-13T104500Z.md one file per pass, never edited after
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The baseline mirrors the paths it records, so `baseline/agents/skills/x/SKILL.md`
|
|
37
|
+
is that skill as the last completed pass left it. Both sides are there on
|
|
38
|
+
purpose: the specification and the feedback say what was **asked for**, the
|
|
39
|
+
wiring and the `agents/` tree say what was **built**, and a pass has to know
|
|
40
|
+
which of the two moved.
|
|
41
|
+
|
|
42
|
+
The baseline is **not a copy of the current specification**. It is the
|
|
43
|
+
specification as the last completed pass left it, which is what makes the
|
|
44
|
+
difference between the two mean "work nobody has done yet".
|
|
45
|
+
|
|
46
|
+
`zen init` creates `baseline/` and `history/` empty, and git does not carry an
|
|
47
|
+
empty directory, so **the presence of `.spec-sync/` proves nothing**. The
|
|
48
|
+
manifest is the signal: no `manifest.txt` means no pass has ever completed.
|
|
49
|
+
|
|
50
|
+
## Deciding the size of the pass
|
|
51
|
+
|
|
52
|
+
Run this first, from the project root:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
.github/skills/zen-spec-sync/scripts/snapshot.sh status
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
It prints one `mode:` line, the `applied:` stamp the baseline belongs to, and a
|
|
59
|
+
`changed:` line per file that differs - specification, feedback, `agents.yaml`
|
|
60
|
+
or anything under `agents/`:
|
|
61
|
+
|
|
62
|
+
| Mode | Means | Do |
|
|
63
|
+
| ------------- | ------------------------------------------ | ------------------------------------------------------------------------------------- |
|
|
64
|
+
| `full` | no manifest - no pass has ever completed | the whole prompt as written, then write the first baseline |
|
|
65
|
+
| `incremental` | a baseline is there and the files differ | scope from the four inputs below |
|
|
66
|
+
| `tampered` | a baseline file does not match its own sha | treat as `full`, and say in the chat which file and that the baseline was not trusted |
|
|
67
|
+
|
|
68
|
+
`tampered` is either a baseline edited by hand or a pass that died between
|
|
69
|
+
copying the files and writing the manifest. Both mean the record is not
|
|
70
|
+
evidence. Do not rewrite `manifest.txt` to make the message go away - that
|
|
71
|
+
asserts a pass happened that did not.
|
|
72
|
+
|
|
73
|
+
`snapshot.sh diff` then prints the unified diffs, and is where the pass
|
|
74
|
+
actually starts.
|
|
75
|
+
|
|
76
|
+
## The four scope inputs
|
|
77
|
+
|
|
78
|
+
An incremental pass is scoped by the union of these. Write the union down before
|
|
79
|
+
editing anything, and report it in the chat as the pass's scope.
|
|
80
|
+
|
|
81
|
+
1. **The `SPECIFICATION.md` diff.** The primary input: added, changed and
|
|
82
|
+
deleted requirements. A deleted requirement is work too - what implemented it
|
|
83
|
+
is now an **extra**, to be reported under `➕` rather than quietly deleted.
|
|
84
|
+
2. **The `SPECIFICATION-FEEDBACK.md` diff.** The file is a questionnaire the
|
|
85
|
+
human answers by ticking one answer's `[ ]` box or deleting the answers they
|
|
86
|
+
do not want. A question that has moved under `✅ Answered` was folded into the
|
|
87
|
+
specification by `/spec-apply-feedback`, and its **Still to implement**
|
|
88
|
+
line is the work. A question with a box marked `[x]`, or one answer left
|
|
89
|
+
under it, and no `✅` entry was answered and never folded in - implement it,
|
|
90
|
+
put its text into `SPECIFICATION.md`, and say so. A new question someone else
|
|
91
|
+
raised is work too. Close what this pass resolves rather than re-raising it.
|
|
92
|
+
3. **What the last pass left open.** The `Still open` section of the entry named
|
|
93
|
+
by `applied:`, in `.spec-sync/history/`. A specification line that has not
|
|
94
|
+
changed since the baseline is **not therefore implemented**: the baseline
|
|
95
|
+
records what the pass applied, and a pass can complete with items open. This
|
|
96
|
+
is the only place that says which.
|
|
97
|
+
4. **The implementation diff.** `agents.yaml` and every file under `agents/` -
|
|
98
|
+
instructions, prompts and skills. This side is what was built rather than
|
|
99
|
+
what was asked for, so it never narrows a pass: it says what moved under you
|
|
100
|
+
since the last one. An agent, tool grant, handoff, model, house rule, prompt
|
|
101
|
+
or skill edited outside a pass **widens** the pass to everything that names
|
|
102
|
+
what changed, and the edit is itself an item - either the specification
|
|
103
|
+
covers it, and you say which line, or it does not, and it is an **extra**
|
|
104
|
+
reported under `➕`.
|
|
105
|
+
|
|
106
|
+
## The four checks a narrow pass may never skip
|
|
107
|
+
|
|
108
|
+
However small the diff:
|
|
109
|
+
|
|
110
|
+
1. **`zen check` passes.** Run it before the first edit and again after the
|
|
111
|
+
last.
|
|
112
|
+
2. **`scripts/_setup.sh` has run to completion in this session, and re-runs
|
|
113
|
+
clean.** A one-line specification change can still break a setup step, and a
|
|
114
|
+
pass that did not run it does not know.
|
|
115
|
+
3. **§9 of the copilot instructions, worked as a checklist**, group by group.
|
|
116
|
+
4. **The capability obligations in §0.1.** For every capability the project has
|
|
117
|
+
turned on, load that capability's skill and re-check what it requires - in
|
|
118
|
+
particular that `agents/memory-instructions.md` is still byte-identical to
|
|
119
|
+
`.github/skills/zen-memory/references/memory-instructions.md`. `zen init` and
|
|
120
|
+
`zen open` rewrite the reference, so this drifts without anybody editing the
|
|
121
|
+
project.
|
|
122
|
+
|
|
123
|
+
A narrow diff narrows reading and editing. It does not narrow verification.
|
|
124
|
+
|
|
125
|
+
## What else an incremental pass still owes
|
|
126
|
+
|
|
127
|
+
The reverse pass of §2 - reading each line and naming the specification item it
|
|
128
|
+
serves - runs over every file this pass touched, plus every file the
|
|
129
|
+
implementation diff named. It is the only pass that finds a line contradicting
|
|
130
|
+
another file, and an edit made this session, or made by hand since the last
|
|
131
|
+
pass, is exactly where a new contradiction comes from.
|
|
132
|
+
|
|
133
|
+
## The history entry
|
|
134
|
+
|
|
135
|
+
One file per pass, `.spec-sync/history/<stamp>.md`, where the stamp is UTC and
|
|
136
|
+
filename-safe: `date -u +%Y-%m-%dT%H%M%SZ`. Never edit a file that is already
|
|
137
|
+
there; a pass that did nothing still writes one saying so.
|
|
138
|
+
|
|
139
|
+
These headings, in this order, because the next pass reads them:
|
|
140
|
+
|
|
141
|
+
```markdown
|
|
142
|
+
# Sync pass 2026-09-13T104500Z
|
|
143
|
+
|
|
144
|
+
- **Mode** - incremental
|
|
145
|
+
- **Spec sha** - 3f9a… (`shasum -a 256 SPECIFICATION.md`)
|
|
146
|
+
|
|
147
|
+
## Scope
|
|
148
|
+
|
|
149
|
+
What drove the pass: the diff hunks, the feedback entries, what the last pass
|
|
150
|
+
left open. `full` if it was a full pass.
|
|
151
|
+
|
|
152
|
+
## Changed
|
|
153
|
+
|
|
154
|
+
One row per file changed, and the specification item it closes.
|
|
155
|
+
|
|
156
|
+
## Feedback
|
|
157
|
+
|
|
158
|
+
Entries opened and entries closed, by number.
|
|
159
|
+
|
|
160
|
+
## Still open
|
|
161
|
+
|
|
162
|
+
Every specification item this pass did **not** close, with its
|
|
163
|
+
`SPECIFICATION-FEEDBACK.md` anchor. Empty means empty - this is what the next
|
|
164
|
+
pass trusts.
|
|
165
|
+
|
|
166
|
+
## Checks
|
|
167
|
+
|
|
168
|
+
`zen check`, each `scripts/_setup.sh` step, and the §9 groups: pass or fail.
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Write the baseline last
|
|
172
|
+
|
|
173
|
+
The one rule that must not be got wrong.
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
.github/skills/zen-spec-sync/scripts/snapshot.sh commit
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
runs **after** `zen check` passes, **after** `scripts/_setup.sh` has completed,
|
|
180
|
+
and **after** the history file is on disk. It takes the newest entry in
|
|
181
|
+
`.spec-sync/history/` - the stamps sort chronologically by name - records it in
|
|
182
|
+
`applied.txt`, and writes `manifest.txt` last of all. It refuses to run when
|
|
183
|
+
there is no history file, and when the newest one is the entry `applied.txt`
|
|
184
|
+
already names: that means this pass wrote no entry of its own.
|
|
185
|
+
|
|
186
|
+
A pass that moves the baseline early and then fails records a specification as
|
|
187
|
+
applied that was never applied - and the next pass diffs against it, sees
|
|
188
|
+
nothing, and skips the work for good. Failing with the baseline unmoved costs
|
|
189
|
+
one repeated pass; failing with it moved costs a requirement that is never
|
|
190
|
+
implemented and that nothing will report. Say in the chat which history file was
|
|
191
|
+
written and that the baseline moved.
|
|
192
|
+
|
|
193
|
+
## The first pass on a project that has no baseline
|
|
194
|
+
|
|
195
|
+
`mode: full`, so run the prompt as written, end to end. Then write the history
|
|
196
|
+
file and commit the baseline exactly as any other pass does - a project only
|
|
197
|
+
becomes incremental by having completed one full pass.
|