-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathMakefile
More file actions
377 lines (352 loc) · 17.5 KB
/
Copy pathMakefile
File metadata and controls
377 lines (352 loc) · 17.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# The full pack set the installed daemon must serve. `make local` verifies the
# freshly built artifact before installation by running verbs() against that
# exact binary. The floor is the current 98-verb production surface: additions
# pass without a Makefile change, while any loss remains fail-closed.
#
# `moodboard` belongs in this list because `build-local` links pack-moodboard,
# so the artifact serves the pack's seven verbs whether or not the list names
# it. Omitting it did not make the gate lenient about a pack it never built; it
# made the gate blind to seven verbs the artifact was already shipping, so
# losing all of them still cleared a floor set below their count.
FULL_PACKS := kg,gtd,memory,comm,schedule,session,workspace,blob,git,knowledge,brain,code,formal,moodboard,tool,exec
LOCAL_VERB_FLOOR := 98
CARGO ?= cargo
LOCAL_BUILD_RECEIPT := crates/target/khive-local-build.json
FLEET_ARTIFACT ?=
# Every variable below can be overridden on the command line (`make VAR=...`)
# and plain `:=`/`?=` loses to command-line assignments. Make performs no
# shell escaping on `$(VAR)`, so a caller-controlled value spliced directly
# into a recipe's shell text (e.g. `$(LOCAL_VERB_FLOOR)`) could break out of
# a quoted argument and run as shell code. Capture the caller's literal value
# before make can expand any `$` bytes, then pass it to the recipe through
# the environment instead of shell source — recipes read `$$<VAR>_VALUE`,
# never `$(VAR)`. `FULL_PACKS` and `LOCAL_VERB_FLOOR` are additionally gated
# by `validate-make-inputs` so a hostile value is rejected before it reaches
# a shell at all; the path/tool variables (LOCAL_BUILD_RECEIPT, CARGO) need no
# character allowlist — the env-value pass alone removes the shell-parse
# surface — but must never be re-spliced as `$(VAR)` into recipe shell text.
# The verification stamp path is likewise never a `:=` derivation — it is
# `"$${LOCAL_BUILD_RECEIPT_VALUE}.verified"`, computed by the shell at recipe
# time from the already-captured env value, so a literal `$` in the receipt
# path is inert data and no `$(shell ...)` payload can run during parsing.
override FLEET_ARTIFACT_VALUE := $(value FLEET_ARTIFACT)
unexport FLEET_ARTIFACT
export FLEET_ARTIFACT_VALUE
override FULL_PACKS_VALUE := $(value FULL_PACKS)
unexport FULL_PACKS
export FULL_PACKS_VALUE
override LOCAL_VERB_FLOOR_VALUE := $(value LOCAL_VERB_FLOOR)
unexport LOCAL_VERB_FLOOR
export LOCAL_VERB_FLOOR_VALUE
override LOCAL_BUILD_RECEIPT_VALUE := $(value LOCAL_BUILD_RECEIPT)
unexport LOCAL_BUILD_RECEIPT
export LOCAL_BUILD_RECEIPT_VALUE
override CARGO_VALUE := $(value CARGO)
unexport CARGO
export CARGO_VALUE
.PHONY: check clippy test contract-test fmt fmt-check build build-local verify-local-artifact validate-make-inputs fleet-build fleet-check clean ci docs-check publish publish-dry local check-fwd bench-1m bench-1m-ci hold-time-gate eval-retrieval-gold-check tz-audit
check:
cd crates && cargo check --workspace
clippy:
cd crates && cargo clippy --workspace --all-targets --all-features -- -D warnings
test:
cd crates && cargo test --workspace
# The all-zone chrono-tz sweep. Ignored by default because it walks every
# zone in the bundled database and costs 48.28s in the debug profile, against
# 3.08s with --release (both measured 2026-08-23). Debug is kept because the
# occasion to run this is a chrono-tz pin move, which invalidates the build
# cache, so an optimized run would pay a cold rebuild to save 45s. It exists
# to be RUN when the
# chrono-tz pin moves, which is when the bundled zone data can change under
# the resolver. Wired to CI on the manifests that carry that pin, so the
# duty is triggered rather than remembered.
tz-audit:
cd crates && cargo test -p khive-runtime --lib tz_database_audit -- --ignored
contract-test:
cd crates && cargo build --release -p kkernel
python3 tests/contract_test.py
fmt:
cd crates && cargo fmt --all
deno fmt docs/
fmt-check:
cd crates && cargo fmt --all -- --check
build:
cd crates && cargo build --workspace --release
# pack-formal and pack-moodboard are listed here because this recipe builds the
# binary an operator actually serves, and both packs are now optional crate
# dependencies rather than unconditional ones. A default `cargo build` links
# neither, which is the point of making them optional; a locally installed
# kkernel still needs them, because FULL_PACKS above names both `formal` and
# `moodboard` and verify-local-artifact runs the artifact with that list — a
# binary without the pack answers `unknown pack name "formal"` and the
# verification gate fails.
# The same applies at runtime to any KHIVE_PACKS naming `formal` or `moodboard`.
build-local:
@echo "==> Building kkernel (release, channel-email, channel-telegram, pack-formal, pack-moodboard)..."
@python3 scripts/build_local_artifact.py \
--cargo "$$CARGO_VALUE" \
--manifest-path crates/Cargo.toml \
--package kkernel \
--features channel-email,channel-telegram,pack-formal,pack-moodboard \
--receipt "$$LOCAL_BUILD_RECEIPT_VALUE"
# Reject a FULL_PACKS/LOCAL_VERB_FLOOR value (from the Makefile default or a
# caller override) that contains anything outside its allowlisted character
# class. Every recipe below reads the sanitized value from the `*_VALUE`
# shell environment variables, never via a raw `$(FULL_PACKS)` /
# `$(LOCAL_VERB_FLOOR)` splice, so this gate is what stands between a
# hostile override and a shell.
validate-make-inputs:
@case "$$FULL_PACKS_VALUE" in \
"") echo "==> ERROR: FULL_PACKS is empty" >&2; exit 1 ;; \
*[!A-Za-z0-9_,-]*) echo "==> ERROR: FULL_PACKS contains characters outside the allowed [A-Za-z0-9_,-] set: $$FULL_PACKS_VALUE" >&2; exit 1 ;; \
esac
@case "$$LOCAL_VERB_FLOOR_VALUE" in \
"") echo "==> ERROR: LOCAL_VERB_FLOOR is empty" >&2; exit 1 ;; \
*[!0-9]*) echo "==> ERROR: LOCAL_VERB_FLOOR must contain digits only: $$LOCAL_VERB_FLOOR_VALUE" >&2; exit 1 ;; \
esac
verify-local-artifact: validate-make-inputs build-local
@python3 scripts/verify_local_artifact.py \
--build-receipt "$$LOCAL_BUILD_RECEIPT_VALUE" \
--packs "$$FULL_PACKS_VALUE" \
--min-verbs "$$LOCAL_VERB_FLOOR_VALUE" \
--stamp "$${LOCAL_BUILD_RECEIPT_VALUE}.verified"
# Build and verify the release artifact without installing it or interrupting
# the serving daemon. This compatibility name makes the build-only safety gate
# discoverable independently of the local-install recipe.
fleet-build: verify-local-artifact
# Re-run the verification probe without rebuilding. By default this checks the
# exact Cargo artifact named by the current build receipt. Set FLEET_ARTIFACT to
# check any executable directly, including the installed kkernel binary:
# make fleet-check FLEET_ARTIFACT="$HOME/.cargo/bin/kkernel"
fleet-check: validate-make-inputs
@if [ -n "$$FLEET_ARTIFACT_VALUE" ]; then \
python3 scripts/verify_local_artifact.py \
--artifact "$$FLEET_ARTIFACT_VALUE" \
--packs "$$FULL_PACKS_VALUE" \
--min-verbs "$$LOCAL_VERB_FLOOR_VALUE"; \
else \
python3 scripts/verify_local_artifact.py \
--build-receipt "$$LOCAL_BUILD_RECEIPT_VALUE" \
--packs "$$FULL_PACKS_VALUE" \
--min-verbs "$$LOCAL_VERB_FLOOR_VALUE"; \
fi
clean:
cd crates && cargo clean
docs-check:
deno fmt --check docs/
check-fwd:
RUSTFLAGS="-D warnings" cargo check --manifest-path crates/khive-merge/Cargo.toml --all-targets
cargo clippy --manifest-path crates/khive-merge/Cargo.toml --all-targets -- -D warnings
cargo test --manifest-path crates/khive-merge/Cargo.toml
ci:
./scripts/ci.sh
publish-dry:
./scripts/publish.sh
publish:
./scripts/publish.sh --live
bench-1m:
@echo "==> Running Vamana 1M scale-proof bench (3-point: 100K/316K/1M, ~7 min)..."
@echo " Set SIFT_DIR to the sift_base.fvecs / sift_query.fvecs directory."
bash scripts/bench_1m.sh
bench-1m-ci:
@echo "==> Running Vamana CI smoke bench (2-point: 10K/50K, <60 s)..."
@echo " Set SIFT_DIR to the sift_base.fvecs / sift_query.fvecs directory."
bash scripts/bench_1m.sh --ci
eval-retrieval-gold-check:
@echo "==> Retrieval eval harness: re-running A_fused_direct against committed gold..."
@echo " (tolerance 0.002 absorbs a pre-existing rank-10-boundary tie-break jitter"
@echo " in memory.recall unrelated to this harness's temporal-weight hermeticity fix"
@echo " -- see benches/retrieval/README.md Determinism section)"
cd benches/retrieval && uv run python evaluate.py --check-gold --gold-tolerance 0.002
hold-time-gate:
@echo "==> ADR-135 F4 release gate: per-shape writer hold-time regression coverage..."
cd crates && cargo test -p khive-pack-comm --test hold_time_regression -- --nocapture
# Python's fcntl.flock uses the same flock(2) lock as the daemon on macOS and Linux.
local: verify-local-artifact
@if ! VERIFIED_ASSIGNMENTS=$$(python3 scripts/verify_local_artifact.py \
--build-receipt "$$LOCAL_BUILD_RECEIPT_VALUE" \
--inspect-stamp "$${LOCAL_BUILD_RECEIPT_VALUE}.verified" \
--min-verbs "$$LOCAL_VERB_FLOOR_VALUE"); then \
exit 1; \
fi; \
if ! eval "$$VERIFIED_ASSIGNMENTS"; then \
echo "==> ERROR: could not load verified-artifact fields"; \
exit 1; \
fi; \
DEST=$$HOME/.cargo/bin/kkernel; \
if [ ! -f "$$SRC" ]; then echo "==> ERROR: build artifact $$SRC missing"; exit 1; fi; \
SRC_SHA256=$$({ shasum -a 256 "$$SRC" 2>/dev/null || sha256sum "$$SRC"; } | awk '{print $$1}'); \
if [ "$$VERIFIED_SHA256" != "$$SRC_SHA256" ]; then \
echo "==> ERROR: build artifact changed after verification! verified=$$VERIFIED_SHA256 current=$$SRC_SHA256"; \
exit 1; \
fi; \
SRC_HASH=$$(md5 -q "$$SRC"); \
SRC_SIZE=$$(stat -f '%z' "$$SRC"); \
echo "==> Source: $$SRC ($$SRC_HASH, $$SRC_SIZE bytes, $$VERIFIED_VERBS verified verbs)"; \
echo "==> Staging + codesigning $$DEST.new..."; \
cp "$$SRC" "$$DEST.new"; \
COPIED_SHA256=$$({ shasum -a 256 "$$DEST.new" 2>/dev/null || sha256sum "$$DEST.new"; } | awk '{print $$1}'); \
if [ "$$VERIFIED_SHA256" != "$$COPIED_SHA256" ]; then \
echo "==> ERROR: staged bytes differ from the verified build artifact! verified=$$VERIFIED_SHA256 staged=$$COPIED_SHA256"; \
rm -f "$$DEST.new"; \
exit 1; \
fi; \
if ! codesign -s - -f "$$DEST.new"; then \
echo "==> ERROR: codesign failed on $$DEST.new — refusing to install"; \
rm -f "$$DEST.new"; \
exit 1; \
fi; \
echo "==> Re-verifying the SIGNED artifact (codesign rewrites the file, so the pre-sign verification does not cover the bytes that get installed)..."; \
if ! python3 scripts/verify_local_artifact.py \
--artifact "$$DEST.new" \
--packs "$$FULL_PACKS_VALUE" \
--min-verbs "$$LOCAL_VERB_FLOOR_VALUE" >/dev/null; then \
echo "==> ERROR: signed artifact failed verification — refusing to install"; \
rm -f "$$DEST.new"; \
exit 1; \
fi; \
SIGNED_SHA256=$$({ shasum -a 256 "$$DEST.new" 2>/dev/null || sha256sum "$$DEST.new"; } | awk '{print $$1}'); \
STAGED_HASH=$$(md5 -q "$$DEST.new"); \
KHIVE_PID_FILE=$${KHIVE_PID:-$$HOME/.khive/khived.pid}; \
SOCK=$${KHIVE_SOCKET:-$$HOME/.khive/khived.sock}; \
OLD_PID=$$(cat "$$KHIVE_PID_FILE" 2>/dev/null | tr -dc "0-9"); \
if [ -n "$$OLD_PID" ] && ! ps -p "$$OLD_PID" >/dev/null 2>&1; then OLD_PID=""; fi; \
if [ -n "$$OLD_PID" ]; then \
OLD_COMM=$$(basename "$$(ps -p "$$OLD_PID" -o comm= 2>/dev/null)" 2>/dev/null); \
if [ "$$OLD_COMM" != "$$(basename "$$DEST")" ]; then \
echo "==> $$KHIVE_PID_FILE names pid $$OLD_PID, but that process is '$$OLD_COMM', not $$(basename "$$DEST"). Stale PID file over a reused PID; treating as no live daemon."; \
OLD_PID=""; \
fi; \
fi; \
OLD_PACKS=""; \
if [ -n "$$OLD_PID" ]; then \
OLD_PACKS=$$(ps -p "$$OLD_PID" -o command= 2>/dev/null | tr " " "\n" | awk 'p{printf " --pack %s", $$0; p=0} /^--pack$$/{p=1}'); \
fi; \
echo "==> Atomically moving into place..."; \
mv "$$DEST.new" "$$DEST"; \
MARKER=$${KHIVE_SUPERVISOR_MARKER:-$$HOME/.khive/khived.supervisor}; \
MARKER_LOCK=$${KHIVE_LOCK:-$$HOME/.khive/khived.recovery.lock}; \
MARKER_PY=$$(printf '%s\n' \
'import fcntl, os, sys, tempfile' \
'action, marker, pid, lock = sys.argv[1:]' \
'label = "make-local"' \
'os.makedirs(os.path.dirname(lock) or ".", exist_ok=True)' \
'with open(lock, "a+b") as lock_file:' \
' fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)' \
' if action == "claim":' \
' if os.path.lexists(marker):' \
' print("foreign")' \
' elif not pid:' \
' print("absent")' \
' else:' \
' parent = os.path.dirname(marker) or "."' \
' os.makedirs(parent, exist_ok=True)' \
' fd, temporary = tempfile.mkstemp(prefix=".khived.supervisor.", dir=parent)' \
' try:' \
' with os.fdopen(fd, "w", encoding="utf-8") as output:' \
' output.write(f"{label}\n{pid}\n10\n")' \
' os.replace(temporary, marker)' \
' finally:' \
' if os.path.exists(temporary):' \
' os.unlink(temporary)' \
' print("owned")' \
' elif action == "release":' \
' try:' \
' with open(marker, "r", encoding="utf-8") as present:' \
' lines = present.read().splitlines()' \
' except FileNotFoundError:' \
' print("absent")' \
' else:' \
' if lines[:2] == [label, pid]:' \
' os.unlink(marker)' \
' print("released")' \
' else:' \
' print("kept")' \
' else:' \
' raise ValueError("unknown marker action")'); \
MARKER_OWNED=""; \
MARKER_FOREIGN=""; \
MARKER_STATUS=$$(python3 -c "$$MARKER_PY" claim "$$MARKER" "$$OLD_PID" "$$MARKER_LOCK") || exit 1; \
release_marker() { \
[ -n "$$MARKER_OWNED" ] || return 0; \
RELEASE_STATUS=$$(python3 -c "$$MARKER_PY" release "$$MARKER" "$$OLD_PID" "$$MARKER_LOCK") || { \
echo "==> ERROR: could not check ownership before releasing $$MARKER" >&2; return 1; \
}; \
MARKER_OWNED=""; \
if [ "$$RELEASE_STATUS" = released ]; then \
echo "==> Released $$MARKER"; \
else \
echo "==> $$MARKER changed or disappeared; leaving it untouched ($$RELEASE_STATUS)"; \
fi; \
}; \
if [ "$$MARKER_STATUS" = owned ]; then \
MARKER_OWNED=1; \
trap 'release_marker' EXIT; trap 'exit 130' INT; trap 'exit 143' TERM; \
echo "==> Claimed the daemon rendezvous with $$MARKER so client requests wait for the replacement instead of spawning a competing daemon"; \
elif [ "$$MARKER_STATUS" = foreign ]; then \
MARKER_FOREIGN=1; \
echo "==> $$MARKER already exists; a supervisor owns this rendezvous. Leaving it untouched."; \
fi; \
if [ -n "$$OLD_PID" ]; then \
echo "==> Stopping daemon pid $$OLD_PID (read from $$KHIVE_PID_FILE before the install)..."; \
kill "$$OLD_PID" 2>/dev/null || true; \
for i in 1 2 3 4 5 6 7 8 9 10; do \
if ps -p "$$OLD_PID" >/dev/null 2>&1; then sleep 1; else break; fi; \
done; \
if ps -p "$$OLD_PID" >/dev/null 2>&1; then \
echo "==> pid $$OLD_PID has not exited after 10s — SIGKILL. This names one process, so it cannot reach a daemon that started during this window."; \
kill -9 "$$OLD_PID" 2>/dev/null || true; \
for i in 1 2 3; do \
if ps -p "$$OLD_PID" >/dev/null 2>&1; then sleep 1; else break; fi; \
done; \
fi; \
else \
echo "==> $$KHIVE_PID_FILE names no live process; nothing to stop."; \
fi; \
if [ -z "$$KHIVE_LOCAL_NO_START" ] && [ -z "$$MARKER_FOREIGN" ]; then \
START_CWD=$${KHIVE_LOCAL_START_CWD:-$$HOME/projects}; \
DLOG="$$HOME/.khive/logs/kkernel-daemon-make-local-$$(date +%Y%m%d-%H%M%S).log"; \
mkdir -p "$$HOME/.khive/logs"; \
if [ -n "$$OLD_PACKS" ]; then \
echo "==> Starting the replacement daemon from $$START_CWD with the outgoing daemon's pack list:$$OLD_PACKS"; \
else \
echo "==> Starting the replacement daemon from $$START_CWD with the built-in default pack set (no outgoing daemon to copy a --pack list from)"; \
fi; \
( cd "$$START_CWD" && exec nohup "$$DEST" mcp --daemon $$OLD_PACKS >> "$$DLOG" 2>&1 & ); \
echo "==> Daemon log: $$DLOG"; \
if [ -n "$$MARKER_OWNED" ]; then \
i=0; SERVING=""; \
while [ $$i -lt 40 ]; do \
if [ -S "$$SOCK" ] && /usr/sbin/lsof -t "$$SOCK" >/dev/null 2>&1; then SERVING=1; break; fi; \
i=$$((i+1)); sleep 0.25; \
done; \
release_marker || exit 1; \
if [ -n "$$SERVING" ]; then \
echo "==> Replacement is serving"; \
else \
echo "==> ERROR: nothing holds $$SOCK 10s after the start. Daemon log tail:"; \
tail -20 "$$DLOG" 2>/dev/null | sed "s/^/ /"; \
exit 1; \
fi; \
fi; \
elif [ -n "$$MARKER_FOREIGN" ]; then \
echo "==> Not starting a replacement daemon: the supervisor named by $$MARKER starts the daemon for this socket, and a second one started here would be a daemon it does not own."; \
else \
release_marker || exit 1; \
echo "==> KHIVE_LOCAL_NO_START set: not starting a replacement daemon; released $$MARKER so clients may spawn."; \
fi; \
DEST_HASH=$$(md5 -q "$$DEST"); \
DEST_SIZE=$$(stat -f '%z' "$$DEST"); \
DEST_MTIME=$$(stat -f '%Sm' "$$DEST"); \
if [ "$$STAGED_HASH" != "$$DEST_HASH" ]; then \
echo "==> ERROR: post-mv hash drift! staged=$$STAGED_HASH dest=$$DEST_HASH"; \
exit 1; \
fi; \
DEST_SHA256=$$({ shasum -a 256 "$$DEST" 2>/dev/null || sha256sum "$$DEST"; } | awk '{print $$1}'); \
if [ "$$SIGNED_SHA256" != "$$DEST_SHA256" ]; then \
echo "==> ERROR: installed bytes differ from the verified signed artifact! signed=$$SIGNED_SHA256 installed=$$DEST_SHA256"; \
exit 1; \
fi; \
echo "==> Installed: $$DEST ($$DEST_HASH, $$DEST_SIZE bytes, $$DEST_MTIME, $$VERIFIED_VERBS verified verbs)"; \
"$$DEST" --version
@echo "==> Done. Run /mcp in Claude Code to reconnect."