@cairnkeep/cli 2.3.1 → 2.4.0
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/CHANGELOG.md +69 -0
- package/README.md +221 -6
- package/bin/cairn +43 -2
- package/claude/capability-contract/hooks/capability-command-finish.sh +81 -0
- package/claude/capability-contract/hooks/capability-command-start.sh +76 -0
- package/claude/hooks/compaction-capture.sh +34 -0
- package/claude/hooks/memory-capture.sh +47 -5
- package/claude/hooks/memory-wakeup.sh +55 -2
- package/docs/building-an-overlay.md +1 -1
- package/docs/design/meta-agent.md +439 -0
- package/docs/learning/lessons/L00-why-cairnkeep.md +1 -1
- package/docs/learning/lessons/L01-safe-trial.md +3 -3
- package/docs/learning/lessons/L02-installation.md +2 -2
- package/docs/learning/lessons/L03-first-project.md +1 -1
- package/docs/operating.md +531 -4
- package/docs/privacy-and-data-flow.md +417 -0
- package/docs/storage.md +252 -0
- package/examples/eval/README.md +42 -0
- package/examples/eval/adapter.json +12 -0
- package/examples/eval/bundled-fake.json +1 -0
- package/examples/eval/task-set.json +1 -0
- package/mcp-memory-server/dist/artifact-cli.js +184 -0
- package/mcp-memory-server/dist/artifact-schema.js +218 -0
- package/mcp-memory-server/dist/artifact-store.js +812 -0
- package/mcp-memory-server/dist/capability-adapter.js +286 -0
- package/mcp-memory-server/dist/capability-cli.js +361 -0
- package/mcp-memory-server/dist/capability-config.js +310 -0
- package/mcp-memory-server/dist/capability-harness.js +496 -0
- package/mcp-memory-server/dist/capability-registry.js +123 -0
- package/mcp-memory-server/dist/capability-schema.js +65 -0
- package/mcp-memory-server/dist/capability-store.js +431 -0
- package/mcp-memory-server/dist/compaction-normalize.js +267 -0
- package/mcp-memory-server/dist/eval-cli.js +523 -0
- package/mcp-memory-server/dist/eval-plan.js +346 -0
- package/mcp-memory-server/dist/eval-process.js +273 -0
- package/mcp-memory-server/dist/eval-report.js +609 -0
- package/mcp-memory-server/dist/eval-runner.js +827 -0
- package/mcp-memory-server/dist/eval-schema.js +368 -0
- package/mcp-memory-server/dist/eval-statistics.js +161 -0
- package/mcp-memory-server/dist/eval-workspace.js +313 -0
- package/mcp-memory-server/dist/failure-signature.js +141 -0
- package/mcp-memory-server/dist/index.js +600 -55
- package/mcp-memory-server/dist/node-cli.js +84 -0
- package/mcp-memory-server/dist/node-schema.js +170 -0
- package/mcp-memory-server/dist/node-store.js +455 -0
- package/mcp-memory-server/dist/note-cli.js +235 -0
- package/mcp-memory-server/dist/note-distiller.js +177 -0
- package/mcp-memory-server/dist/note-enrichment.js +93 -0
- package/mcp-memory-server/dist/note-schema.js +4 -0
- package/mcp-memory-server/dist/note-store.js +985 -0
- package/mcp-memory-server/dist/trajectory-cli.js +141 -0
- package/mcp-memory-server/dist/trajectory-normalize.js +448 -0
- package/mcp-memory-server/dist/trajectory-redaction.js +155 -0
- package/mcp-memory-server/dist/trajectory-schema.js +73 -0
- package/mcp-memory-server/dist/trajectory-store.js +339 -0
- package/mcp-memory-server/package.json +24 -2
- package/mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.219.json +9 -0
- package/mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.220.json +9 -0
- package/mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-event.json +6 -0
- package/mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-messages.json +119 -0
- package/mcp-memory-server/scripts/fixtures/compaction-unknown-version.json +17 -0
- package/opencode/capability-contract/plugins/capability-command.ts +319 -0
- package/opencode/command/graphify.md +25 -14
- package/opencode/plugins/memory-capture.ts +76 -10
- package/opencode/plugins/memory-wakeup.ts +58 -6
- package/package.json +8 -4
- package/pi/extensions/cairnkeep-trajectory.ts +55 -0
- package/schemas/artifact.schema.json +133 -0
- package/schemas/capability-callback.schema.json +68 -0
- package/schemas/capability-contract.schema.json +126 -0
- package/schemas/eval-adapter.schema.json +43 -0
- package/schemas/eval-protocol.schema.json +114 -0
- package/schemas/eval-report.schema.json +423 -0
- package/schemas/eval-task-set.schema.json +114 -0
- package/schemas/memory-node.schema.json +90 -0
- package/schemas/note.schema.json +112 -0
- package/schemas/trajectory-redaction.schema.json +25 -0
- package/schemas/trajectory.schema.json +82 -0
- package/scripts/bootstrap.sh +4 -0
- package/scripts/completion.sh +119 -5
- package/scripts/doctor.sh +284 -0
- package/scripts/fake-eval-adapter.mjs +120 -0
- package/scripts/fixtures/capability-harness-contracts.json +408 -0
- package/scripts/install-audit-timer.sh +3 -1
- package/scripts/lib/capability-opencode-plugin-harness.mjs +291 -0
- package/scripts/lib/trajectory-opencode-plugin-harness.mjs +25 -0
- package/scripts/lib/trajectory-pi-extension-harness.mjs +42 -0
- package/scripts/memory-wiki-audit.sh +49 -0
- package/scripts/smoke-bash32.sh +9 -0
- package/scripts/sync-claude-assets.sh +99 -6
- package/scripts/sync-opencode-graphify-assets.sh +24 -3
- package/scripts/sync-opencode-plugin-assets.sh +31 -5
- package/scripts/sync-opencode-security-assets.sh +24 -3
- package/scripts/sync-opencode-wiki-assets.sh +24 -3
- package/scripts/sync-pi-assets.sh +72 -0
- package/scripts/test-audit-timer.sh +98 -0
- package/scripts/test-bootstrap-untracked.sh +48 -0
- package/scripts/test-cli-dispatch.sh +41 -2
- package/scripts/test-compaction-hooks.sh +495 -0
- package/scripts/test-completion.sh +77 -0
- package/scripts/test-doctor.sh +348 -1
- package/scripts/test-eval-cli.sh +175 -0
- package/scripts/test-launcher-hooks.sh +602 -3
- package/scripts/test-notes-cli.sh +114 -0
- package/scripts/test-package-install.sh +251 -0
- package/scripts/test-phase16-node-lifecycle.sh +50 -0
- package/scripts/test-phase17-artifact-lifecycle.sh +370 -0
- package/scripts/test-phase18-capability-lifecycle.sh +504 -0
- package/scripts/test-phase18-harness-boundary.sh +1060 -0
- package/scripts/test-phase19-eval-lifecycle.sh +1015 -0
- package/scripts/test-trajectory-cli.sh +87 -0
- package/scripts/test-trajectory-hooks.sh +142 -0
- package/scripts/test-uninstall.sh +231 -5
- package/scripts/uninstall.sh +37 -5
- package/scripts/verify-dependency-security.mjs +121 -0
- package/scripts/verify-docs-parity.sh +527 -1
- package/scripts/verify-phase18-runtime-evidence.mjs +895 -0
- package/scripts/verify-phase19-runtime-evidence.mjs +947 -0
- package/templates/capabilities.json.template +7 -0
- package/templates/env.example.template +51 -0
- package/templates/memory-wiki-audit.service.template +1 -1
- package/templates/start-claude.sh.template +36 -0
- package/templates/start-opencode.sh.template +47 -5
- package/templates/start-pi.sh.template +30 -0
- package/templates/trajectory-redaction.json.template +4 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Phase 17 package/operator/reversibility contract. The default invocation
|
|
3
|
+
# exercises the complete shipped lifecycle; the historical RED gate is explicit.
|
|
4
|
+
set -uo pipefail
|
|
5
|
+
|
|
6
|
+
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
7
|
+
MODE="${1:-}"
|
|
8
|
+
EXPECTED_RED_EXIT=86
|
|
9
|
+
RED_MARKER="PHASE17_RED:ARTIFACT_PACKAGE_LIFECYCLE_MISSING"
|
|
10
|
+
|
|
11
|
+
case "$MODE" in
|
|
12
|
+
""|--expect-red) ;;
|
|
13
|
+
*) echo "Usage: $0 [--expect-red]" >&2; exit 2 ;;
|
|
14
|
+
esac
|
|
15
|
+
[[ "$MODE" != "" || "$#" -eq 0 ]] || exit 2
|
|
16
|
+
[[ "$MODE" != "--expect-red" || "$#" -eq 1 ]] || exit 2
|
|
17
|
+
|
|
18
|
+
tmp=$(mktemp -d)
|
|
19
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
20
|
+
baseline_number=0
|
|
21
|
+
missing=0
|
|
22
|
+
|
|
23
|
+
fail() {
|
|
24
|
+
echo "FAIL: $1" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
mark_missing() {
|
|
29
|
+
missing=$((missing + 1))
|
|
30
|
+
echo "MISSING: $1" >&2
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
run_baseline_check() {
|
|
34
|
+
baseline_number=$((baseline_number + 1))
|
|
35
|
+
local output="$tmp/baseline-$baseline_number.out"
|
|
36
|
+
if ! "$@" >"$output" 2>&1; then
|
|
37
|
+
cat "$output" >&2
|
|
38
|
+
fail "baseline failed: $*"
|
|
39
|
+
fi
|
|
40
|
+
if grep -qF 'PHASE17_RED:' "$output"; then
|
|
41
|
+
cat "$output" >&2
|
|
42
|
+
fail "baseline emitted a Phase 17 RED marker: $*"
|
|
43
|
+
fi
|
|
44
|
+
cat "$output"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
run_shell_syntax_baseline() {
|
|
48
|
+
local script
|
|
49
|
+
while IFS= read -r script; do
|
|
50
|
+
bash -n "$script" || return 1
|
|
51
|
+
done < <(find "$ROOT/bin" "$ROOT/scripts" -type f \( -name '*.sh' -o -name 'cairn' \) | sort)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
run_baseline() {
|
|
55
|
+
run_baseline_check "$ROOT/scripts/test-package-install.sh"
|
|
56
|
+
run_baseline_check "$ROOT/scripts/test-uninstall.sh"
|
|
57
|
+
run_baseline_check "$ROOT/scripts/test-doctor.sh"
|
|
58
|
+
run_baseline_check "$ROOT/scripts/test-cli-dispatch.sh"
|
|
59
|
+
run_baseline_check "$ROOT/scripts/test-completion.sh"
|
|
60
|
+
run_baseline_check "$ROOT/scripts/test-runtime-contract.sh"
|
|
61
|
+
run_baseline_check run_shell_syntax_baseline
|
|
62
|
+
run_baseline_check "$ROOT/scripts/test-portable-sh.sh"
|
|
63
|
+
echo "PASS: Phase 17 package/operator pre-feature baseline"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
require_term() {
|
|
67
|
+
local path="$1"
|
|
68
|
+
local term="$2"
|
|
69
|
+
local label="$3"
|
|
70
|
+
if ! grep -qiF "$term" "$path"; then
|
|
71
|
+
mark_missing "$label"
|
|
72
|
+
fi
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
require_pattern() {
|
|
76
|
+
local path="$1"
|
|
77
|
+
local pattern="$2"
|
|
78
|
+
local label="$3"
|
|
79
|
+
if ! grep -qiE "$pattern" "$path"; then
|
|
80
|
+
mark_missing "$label"
|
|
81
|
+
fi
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
inspect_package() {
|
|
85
|
+
local listing="$tmp/pack.json"
|
|
86
|
+
local paths="$tmp/pack-paths.txt"
|
|
87
|
+
if ! (cd "$ROOT" && npm pack --dry-run --json --ignore-scripts) >"$listing" 2>"$tmp/pack.err"; then
|
|
88
|
+
cat "$tmp/pack.err" >&2
|
|
89
|
+
fail "npm pack dry-run failed"
|
|
90
|
+
fi
|
|
91
|
+
if ! node - "$listing" >"$paths" <<'NODE'
|
|
92
|
+
const fs = require("fs");
|
|
93
|
+
const input = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
|
94
|
+
if (!Array.isArray(input) || input.length !== 1 || !Array.isArray(input[0].files)) {
|
|
95
|
+
throw new Error("unexpected npm pack JSON shape");
|
|
96
|
+
}
|
|
97
|
+
for (const entry of input[0].files) {
|
|
98
|
+
if (!entry || typeof entry.path !== "string") throw new Error("invalid npm pack file entry");
|
|
99
|
+
process.stdout.write(`${entry.path}\n`);
|
|
100
|
+
}
|
|
101
|
+
NODE
|
|
102
|
+
then
|
|
103
|
+
fail "npm pack dry-run JSON could not be parsed"
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
local expected
|
|
107
|
+
for expected in \
|
|
108
|
+
schemas/artifact.schema.json \
|
|
109
|
+
mcp-memory-server/dist/artifact-schema.js \
|
|
110
|
+
mcp-memory-server/dist/artifact-store.js \
|
|
111
|
+
mcp-memory-server/dist/compaction-normalize.js \
|
|
112
|
+
mcp-memory-server/dist/artifact-cli.js \
|
|
113
|
+
claude/hooks/compaction-capture.sh \
|
|
114
|
+
opencode/plugins/memory-capture.ts \
|
|
115
|
+
opencode/plugins/memory-wakeup.ts \
|
|
116
|
+
mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.219.json \
|
|
117
|
+
mcp-memory-server/scripts/fixtures/compaction-claude-code-2.1.220.json \
|
|
118
|
+
mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-event.json \
|
|
119
|
+
mcp-memory-server/scripts/fixtures/compaction-opencode-1.17.20-messages.json
|
|
120
|
+
do
|
|
121
|
+
grep -qxF "$expected" "$paths" || mark_missing "npm package asset $expected"
|
|
122
|
+
done
|
|
123
|
+
|
|
124
|
+
if node - "$paths" <<'NODE'
|
|
125
|
+
const fs = require("fs");
|
|
126
|
+
const paths = fs.readFileSync(process.argv[2], "utf8").trim().split("\n").filter(Boolean);
|
|
127
|
+
const forbidden = paths.filter((path) =>
|
|
128
|
+
/(^|\/)\.agentfs\//.test(path)
|
|
129
|
+
|| /\.db(?:-wal|-shm)?$/i.test(path)
|
|
130
|
+
|| /(^|\/)runtime-evidence\//.test(path)
|
|
131
|
+
|| /(^|\/)(?:generated-)?artifacts?\/(?:projects|sessions|data)\//i.test(path)
|
|
132
|
+
);
|
|
133
|
+
if (forbidden.length > 0) {
|
|
134
|
+
for (const path of forbidden) console.error(`forbidden packaged artifact data: ${path}`);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
NODE
|
|
138
|
+
then
|
|
139
|
+
:
|
|
140
|
+
else
|
|
141
|
+
fail "npm package contains a database, sidecar, or generated artifact data"
|
|
142
|
+
fi
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
check_cli_and_completion() {
|
|
146
|
+
local help="$tmp/cairn-help.out"
|
|
147
|
+
"$ROOT/bin/cairn" help >"$help" 2>"$tmp/cairn-help.err" || fail "cairn help failed"
|
|
148
|
+
require_term "$help" 'cairn artifact <list|show|delete|prune>' "artifact CLI help tree"
|
|
149
|
+
|
|
150
|
+
if grep -qF 'cairn artifact <list|show|delete|prune>' "$help"; then
|
|
151
|
+
if ! "$ROOT/bin/cairn" artifact --help >"$tmp/artifact-help.out" 2>"$tmp/artifact-help.err"; then
|
|
152
|
+
cat "$tmp/artifact-help.err" >&2
|
|
153
|
+
fail "advertised artifact CLI dispatch is broken"
|
|
154
|
+
fi
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
local shell output
|
|
158
|
+
for shell in bash zsh fish; do
|
|
159
|
+
output="$tmp/completion-$shell.out"
|
|
160
|
+
"$ROOT/bin/cairn" completion "$shell" >"$output" || fail "$shell completion generation failed"
|
|
161
|
+
require_pattern "$output" 'artifact.*list.*show.*delete.*prune' "$shell completion artifact list/show/delete/prune tree"
|
|
162
|
+
done
|
|
163
|
+
|
|
164
|
+
require_term "$ROOT/scripts/test-cli-dispatch.sh" 'artifact' "artifact CLI dispatch regression coverage"
|
|
165
|
+
require_term "$ROOT/scripts/test-completion.sh" 'artifact' "artifact completion regression coverage"
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
check_doctor_contract() {
|
|
169
|
+
require_term "$ROOT/scripts/doctor.sh" 'artifact_cli=' "doctor artifact CLI integration"
|
|
170
|
+
require_term "$ROOT/scripts/doctor.sh" 'artifact store (not present' "doctor absent artifact state"
|
|
171
|
+
require_term "$ROOT/scripts/doctor.sh" 'artifact store integrity' "doctor valid artifact state"
|
|
172
|
+
require_term "$ROOT/scripts/doctor.sh" 'artifact store repaired' "doctor derived-state repair"
|
|
173
|
+
require_pattern "$ROOT/scripts/doctor.sh" 'artifact.*could not be repaired safely|artifact.*authoritative.*corrupt' "doctor authoritative corruption failure"
|
|
174
|
+
require_term "$ROOT/scripts/test-doctor.sh" 'artifacts.db' "doctor artifact database fixtures"
|
|
175
|
+
require_pattern "$ROOT/scripts/test-doctor.sh" 'derived.*repair|repair.*derived' "doctor derived repair regression case"
|
|
176
|
+
require_pattern "$ROOT/scripts/test-doctor.sh" 'authoritative.*(fail|corrupt)|corrupt.*authoritative' "doctor authoritative failure regression case"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
check_env_contract() {
|
|
180
|
+
local env_template="$ROOT/templates/env.example.template"
|
|
181
|
+
local expected key
|
|
182
|
+
for expected in \
|
|
183
|
+
'CAIRN_COMPACTION_CAPTURE=1' \
|
|
184
|
+
'CAIRN_ARTIFACT_STORE=1' \
|
|
185
|
+
'CAIRN_ARTIFACT_HTTP=1' \
|
|
186
|
+
'CAIRN_ARTIFACT_MAX_BYTES=1048576' \
|
|
187
|
+
'CAIRN_ARTIFACT_SESSION_MAX_BYTES=16777216' \
|
|
188
|
+
'CAIRN_ARTIFACT_STORE_MAX_BYTES=268435456' \
|
|
189
|
+
'CAIRN_ARTIFACT_RETENTION_DAYS=30' \
|
|
190
|
+
'CAIRN_COMPACTION_MAX_REVISIONS=8' \
|
|
191
|
+
'CAIRN_ARTIFACT_GENERATED_FILE_SNAPSHOT_MAX_BYTES=262144'
|
|
192
|
+
do
|
|
193
|
+
grep -qxF "# $expected" "$env_template" || mark_missing "commented env default $expected"
|
|
194
|
+
key=${expected%%=*}
|
|
195
|
+
if grep -qE "^[[:space:]]*${key}=" "$env_template"; then
|
|
196
|
+
fail "$key is enabled in the environment template"
|
|
197
|
+
fi
|
|
198
|
+
done
|
|
199
|
+
require_pattern "$env_template" 'off by default|default-off|disabled by default' "artifact and compaction default-off env guidance"
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
check_uninstall_bytes() {
|
|
203
|
+
local fixture_home="$tmp/uninstall-home"
|
|
204
|
+
local fixture_bin="$tmp/uninstall-bin"
|
|
205
|
+
local project="$tmp/uninstall-project"
|
|
206
|
+
local live="$tmp/uninstall-live"
|
|
207
|
+
local pi_live="$tmp/uninstall-pi-live"
|
|
208
|
+
local original="$tmp/artifacts.original"
|
|
209
|
+
local bundle backup_copy
|
|
210
|
+
|
|
211
|
+
mkdir -p "$fixture_home" "$fixture_bin" "$project/.ai" "$project/.agentfs" "$live" "$pi_live"
|
|
212
|
+
printf '#!/usr/bin/env bash\nexit 0\n' >"$fixture_bin/claude"
|
|
213
|
+
printf '#!/usr/bin/env bash\nexit 0\n' >"$fixture_bin/systemctl"
|
|
214
|
+
chmod +x "$fixture_bin/claude" "$fixture_bin/systemctl"
|
|
215
|
+
git -C "$project" init -q || fail "uninstall fixture git init failed"
|
|
216
|
+
printf 'artifact-v1\000durable\377bytes\n' >"$project/.agentfs/artifacts.db"
|
|
217
|
+
cp "$project/.agentfs/artifacts.db" "$original"
|
|
218
|
+
|
|
219
|
+
HOME="$fixture_home" XDG_CONFIG_HOME="$fixture_home/.config" PATH="$fixture_bin:$PATH" \
|
|
220
|
+
"$ROOT/scripts/uninstall.sh" --dry-run --live-root "$live" --pi-live-root "$pi_live" "$project" \
|
|
221
|
+
>"$tmp/uninstall-dry-run.out" 2>&1 || fail "artifact uninstall dry-run failed"
|
|
222
|
+
cmp -s "$original" "$project/.agentfs/artifacts.db" || fail "uninstall dry-run mutated artifact bytes"
|
|
223
|
+
if find "$fixture_home" -maxdepth 1 -name '.cairnkeep-uninstall-*' | grep -q .; then
|
|
224
|
+
fail "uninstall dry-run created a backup bundle"
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
HOME="$fixture_home" XDG_CONFIG_HOME="$fixture_home/.config" PATH="$fixture_bin:$PATH" \
|
|
228
|
+
"$ROOT/scripts/uninstall.sh" --yes --live-root "$live" --pi-live-root "$pi_live" "$project" \
|
|
229
|
+
>"$tmp/uninstall-keep.out" 2>&1 || fail "default artifact uninstall failed"
|
|
230
|
+
cmp -s "$original" "$project/.agentfs/artifacts.db" || fail "default uninstall did not retain exact artifact bytes"
|
|
231
|
+
|
|
232
|
+
HOME="$fixture_home" XDG_CONFIG_HOME="$fixture_home/.config" PATH="$fixture_bin:$PATH" \
|
|
233
|
+
"$ROOT/scripts/uninstall.sh" --yes --purge-memory --live-root "$live" --pi-live-root "$pi_live" "$project" \
|
|
234
|
+
>"$tmp/uninstall-purge.out" 2>&1 || fail "artifact purge uninstall failed"
|
|
235
|
+
[[ ! -e "$project/.agentfs" ]] || fail "artifact purge left the project store in place"
|
|
236
|
+
bundle=$(find "$fixture_home" -maxdepth 1 -type d -name '.cairnkeep-uninstall-*' | sort | tail -1)
|
|
237
|
+
[[ -n "$bundle" && -x "$bundle/revert.sh" ]] || fail "artifact purge did not generate a revert script"
|
|
238
|
+
backup_copy="$bundle/files/${project#/}/.agentfs/artifacts.db"
|
|
239
|
+
cmp -s "$original" "$backup_copy" || fail "artifact purge backup did not preserve exact bytes"
|
|
240
|
+
HOME="$fixture_home" XDG_CONFIG_HOME="$fixture_home/.config" PATH="$fixture_bin:$PATH" \
|
|
241
|
+
bash "$bundle/revert.sh" >"$tmp/uninstall-revert.out" 2>&1 || fail "artifact revert failed"
|
|
242
|
+
cmp -s "$original" "$project/.agentfs/artifacts.db" || fail "artifact revert did not restore exact bytes"
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
check_docs_contract() {
|
|
246
|
+
local public_doc
|
|
247
|
+
for public_doc in "$ROOT/README.md" "$ROOT/docs/operating.md"; do
|
|
248
|
+
require_term "$public_doc" 'CAIRN_COMPACTION_CAPTURE' "$(basename "$public_doc") compaction flag"
|
|
249
|
+
require_term "$public_doc" 'CAIRN_ARTIFACT_STORE' "$(basename "$public_doc") artifact-store flag"
|
|
250
|
+
require_term "$public_doc" 'CAIRN_ARTIFACT_HTTP' "$(basename "$public_doc") artifact HTTP flag"
|
|
251
|
+
require_term "$public_doc" 'cairn artifact' "$(basename "$public_doc") artifact CLI"
|
|
252
|
+
require_term "$public_doc" 'artifact_write' "$(basename "$public_doc") artifact_write tool"
|
|
253
|
+
require_term "$public_doc" 'artifact_read' "$(basename "$public_doc") artifact_read tool"
|
|
254
|
+
require_term "$public_doc" 'artifact_list' "$(basename "$public_doc") artifact_list tool"
|
|
255
|
+
require_term "$public_doc" 'artifact_delete' "$(basename "$public_doc") artifact_delete tool"
|
|
256
|
+
require_pattern "$public_doc" 'CAIRN_(COMPACTION_CAPTURE|ARTIFACT_STORE)[^.]{0,240}(off by default|default-off|disabled by default)|(off by default|default-off|disabled by default)[^.]{0,240}CAIRN_(COMPACTION_CAPTURE|ARTIFACT_STORE)' "$(basename "$public_doc") disabled behavior"
|
|
257
|
+
done
|
|
258
|
+
|
|
259
|
+
local storage="$ROOT/docs/storage.md"
|
|
260
|
+
require_term "$storage" '.agentfs/artifacts.db' "storage local artifact location"
|
|
261
|
+
require_term "$storage" 'server-side' "storage server-side remote location"
|
|
262
|
+
require_term "$storage" 'X-Cairn-Project' "storage remote project identity"
|
|
263
|
+
require_pattern "$storage" 'artifact.*(index|indexes)|index.*artifact' "storage artifact indexes"
|
|
264
|
+
require_pattern "$storage" 'artifact.*retention|retention.*artifact' "storage artifact retention"
|
|
265
|
+
require_pattern "$storage" 'artifact.*backup|backup.*artifact' "storage artifact backup"
|
|
266
|
+
require_pattern "$storage" 'artifact.*migration|migration.*artifact|no migration' "storage artifact migration"
|
|
267
|
+
|
|
268
|
+
local privacy="$ROOT/docs/privacy-and-data-flow.md"
|
|
269
|
+
require_term "$privacy" 'PostCompact' "privacy Claude local compaction flow"
|
|
270
|
+
require_term "$privacy" 'session.compacted' "privacy OpenCode local compaction flow"
|
|
271
|
+
require_pattern "$privacy" 'artifact.*stdio|stdio.*artifact' "privacy local stdio artifact flow"
|
|
272
|
+
require_term "$privacy" 'CAIRN_ARTIFACT_HTTP' "privacy explicit HTTP artifact flow"
|
|
273
|
+
require_pattern "$privacy" 'redact[^.]{0,160}(before|then)[^.]{0,160}(digest|index|write)' "privacy redaction-before-derived-data order"
|
|
274
|
+
require_pattern "$privacy" 'artifact[^.]{0,220}no default egress|no default egress[^.]{0,220}artifact' "privacy no-default-egress statement"
|
|
275
|
+
require_pattern "$privacy" 'artifact[^.]{0,220}(no telemetry|telemetry.*none|without telemetry)|(no telemetry|telemetry.*none|without telemetry)[^.]{0,220}artifact' "privacy no-telemetry statement"
|
|
276
|
+
require_pattern "$privacy" 'raw[^.]{0,100}(on demand|on-demand)|on-demand[^.]{0,100}raw' "privacy raw-summary-on-demand-only recovery"
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
check_dependency_delta() {
|
|
280
|
+
local first_phase_commit last_phase_commit phase_base
|
|
281
|
+
first_phase_commit=$(git -C "$ROOT" log --reverse --format='%H' --grep='^test(17-01):' HEAD | head -1)
|
|
282
|
+
[[ -n "$first_phase_commit" ]] || fail "cannot resolve the first Phase 17 source commit"
|
|
283
|
+
last_phase_commit=$(git -C "$ROOT" log --format='%H %s' HEAD \
|
|
284
|
+
| awk '$2 ~ /^[a-z]+\(17-[0-9]+\):$/ { print $1; exit }')
|
|
285
|
+
[[ -n "$last_phase_commit" ]] || fail "cannot resolve the last Phase 17 source commit"
|
|
286
|
+
phase_base=$(git -C "$ROOT" rev-parse "$first_phase_commit^") || fail "cannot resolve the pre-Phase 17 base"
|
|
287
|
+
|
|
288
|
+
if ! node - "$ROOT" "$phase_base" "$last_phase_commit" <<'NODE'
|
|
289
|
+
const { execFileSync } = require("child_process");
|
|
290
|
+
const [root, base, terminal] = process.argv.slice(2);
|
|
291
|
+
const loadAt = (commit, path) => JSON.parse(execFileSync(
|
|
292
|
+
"git",
|
|
293
|
+
["-C", root, "show", `${commit}:${path}`],
|
|
294
|
+
{ encoding: "utf8" },
|
|
295
|
+
));
|
|
296
|
+
const normalize = (value) => Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)));
|
|
297
|
+
const selected = (value, includeVersion) => ({
|
|
298
|
+
...(includeVersion ? { version: value.version } : {}),
|
|
299
|
+
dependencies: normalize(value.dependencies ?? {}),
|
|
300
|
+
devDependencies: normalize(value.devDependencies ?? {}),
|
|
301
|
+
optionalDependencies: normalize(value.optionalDependencies ?? {}),
|
|
302
|
+
peerDependencies: normalize(value.peerDependencies ?? {}),
|
|
303
|
+
});
|
|
304
|
+
for (const [path, includeVersion] of [["package.json", true], ["mcp-memory-server/package.json", false]]) {
|
|
305
|
+
const before = selected(loadAt(base, path), includeVersion);
|
|
306
|
+
const after = selected(loadAt(terminal, path), includeVersion);
|
|
307
|
+
if (JSON.stringify(before) !== JSON.stringify(after)) {
|
|
308
|
+
console.error(`dependency or version delta from pre-phase base: ${path}`);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
NODE
|
|
313
|
+
then
|
|
314
|
+
fail "package dependency/version declarations changed during Phase 17"
|
|
315
|
+
fi
|
|
316
|
+
git -C "$ROOT" diff --quiet "$phase_base" "$last_phase_commit" -- package-lock.json mcp-memory-server/package-lock.json \
|
|
317
|
+
|| fail "lockfile changed during Phase 17"
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
check_runtime_evidence() {
|
|
321
|
+
local evidence_dir="$ROOT/.planning/phases/17-compaction-capture-and-artifact-store/runtime-evidence"
|
|
322
|
+
local validation="$ROOT/.planning/phases/17-compaction-capture-and-artifact-store/17-VALIDATION.md"
|
|
323
|
+
local version log
|
|
324
|
+
for version in 22 24 26; do
|
|
325
|
+
log="$evidence_dir/node$version.log"
|
|
326
|
+
if [[ ! -s "$log" ]]; then
|
|
327
|
+
mark_missing "actual Node $version runtime evidence"
|
|
328
|
+
else
|
|
329
|
+
grep -qE 'PASS|passed|test:smoke' "$log" || fail "Node $version evidence does not record a passing changed suite"
|
|
330
|
+
fi
|
|
331
|
+
require_pattern "$validation" "Node $version observed GREEN at commit [0-9a-f]{7,40}" "Node $version tested-commit validation wording"
|
|
332
|
+
done
|
|
333
|
+
log="$evidence_dir/bash32.log"
|
|
334
|
+
if [[ ! -s "$log" ]]; then
|
|
335
|
+
mark_missing "actual Bash 3.2 runtime evidence"
|
|
336
|
+
else
|
|
337
|
+
grep -qF 'smoke-bash32: OK' "$log" || fail "Bash 3.2 evidence does not record a passing smoke run"
|
|
338
|
+
fi
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
run_baseline
|
|
342
|
+
inspect_package
|
|
343
|
+
check_cli_and_completion
|
|
344
|
+
check_doctor_contract
|
|
345
|
+
check_env_contract
|
|
346
|
+
check_uninstall_bytes
|
|
347
|
+
check_docs_contract
|
|
348
|
+
check_dependency_delta
|
|
349
|
+
|
|
350
|
+
if [[ "$missing" -gt 0 ]]; then
|
|
351
|
+
if [[ "$MODE" == "--expect-red" ]]; then
|
|
352
|
+
printf '%s\n' "$RED_MARKER" >&2
|
|
353
|
+
exit "$EXPECTED_RED_EXIT"
|
|
354
|
+
fi
|
|
355
|
+
fail "Phase 17 package/operator lifecycle contract is incomplete"
|
|
356
|
+
fi
|
|
357
|
+
|
|
358
|
+
if [[ "$MODE" != "--expect-red" ]]; then
|
|
359
|
+
echo "PASS: Phase 17 package/operator lifecycle contract"
|
|
360
|
+
exit 0
|
|
361
|
+
fi
|
|
362
|
+
|
|
363
|
+
check_runtime_evidence
|
|
364
|
+
if [[ "$missing" -gt 0 ]]; then
|
|
365
|
+
printf '%s\n' "$RED_MARKER" >&2
|
|
366
|
+
exit "$EXPECTED_RED_EXIT"
|
|
367
|
+
fi
|
|
368
|
+
|
|
369
|
+
echo "Phase 17 package/operator lifecycle is already complete; expected RED is stale" >&2
|
|
370
|
+
exit 1
|