@junghanacs/entwurf 0.12.8 → 0.12.9
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/AGENTS.md +4 -4
- package/CHANGELOG.md +20 -1
- package/DELIVERY.md +1 -1
- package/README.md +122 -10
- package/demo/README.md +2 -2
- package/docs/setup-clean-host.md +14 -3
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +9 -15
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +12 -12
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +5 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn-production.js +8 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +15 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +483 -34
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook.js +8 -3
- package/mcp/entwurf-bridge/dist/scripts/agy-imprint.js +14 -2
- package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +155 -29
- package/mcp/entwurf-bridge/src/index.ts +10 -12
- package/package.json +7 -7
- package/pi-extensions/entwurf-control.ts +12 -12
- package/pi-extensions/lib/acp/models.ts +12 -12
- package/pi-extensions/lib/entwurf-fact-provider.ts +9 -2
- package/pi-extensions/lib/entwurf-v2-production.ts +8 -2
- package/pi-extensions/lib/entwurf-v2-spawn-production.ts +8 -2
- package/pi-extensions/lib/meta-sender-identity.ts +15 -5
- package/pi-extensions/lib/meta-session.ts +526 -38
- package/pi-extensions/meta-bridge-hook.ts +8 -2
- package/run.sh +30 -26
- package/scripts/agy-imprint.ts +15 -1
- package/scripts/check-acp-session-reuse.ts +1 -1
- package/scripts/check-acp-session-store.ts +3 -3
- package/scripts/check-agy-sender-identity.ts +83 -0
- package/scripts/check-entwurf-control-rpc.ts +2 -2
- package/scripts/check-entwurf-fact-provider.ts +9 -1
- package/scripts/check-entwurf-facts.ts +1 -1
- package/scripts/check-entwurf-mailbox-guard.ts +6 -2
- package/scripts/check-entwurf-resume-args.ts +6 -6
- package/scripts/check-entwurf-session-identity.ts +7 -6
- package/scripts/check-entwurf-v2-production.ts +4 -2
- package/scripts/check-entwurf-v2-spawn-production.ts +2 -2
- package/scripts/check-fresh-cut-gate.sh +305 -4
- package/scripts/check-meta-identity-consumers.ts +501 -1
- package/scripts/check-meta-listing.ts +91 -9
- package/scripts/check-meta-receiver-marker.ts +54 -0
- package/scripts/check-model-lock.ts +1 -1
- package/scripts/meta-bridge-fresh-cut.ts +164 -28
- package/scripts/pi_settings_io.py +65 -0
- package/scripts/register-pi-package.py +183 -37
- package/scripts/register-pi-provider.py +68 -10
- package/scripts/smoke-acp-socket-citizen-live.ts +2 -2
- package/scripts/smoke-meta-install-state.sh +1 -1
- package/scripts/smoke-pi-attach.ts +7 -2
- package/scripts/smoke-user-scope-citizen.sh +177 -0
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
# statusline `?` had a tripwire; THIS omission had none and hid until GLG hit it
|
|
10
10
|
# in another repo. This gate is that missing tripwire: if the registration ever
|
|
11
11
|
# regresses, `pnpm check` goes red.
|
|
12
|
+
#
|
|
13
|
+
# Cells 11–13 close the second omission (#53 B): every case here drove a fake
|
|
14
|
+
# settings file with ABSOLUTE entries, so the portable, settings-relative form this
|
|
15
|
+
# repo actually commits was never registered against — and `setup` duplicated and
|
|
16
|
+
# restyled the tracked file for four cuts without a single gate seeing it.
|
|
12
17
|
set -euo pipefail
|
|
13
18
|
|
|
14
19
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -187,5 +192,177 @@ assert d['defaultProvider']=='openai-codex', 'run.sh remove-user-scope dropped a
|
|
|
187
192
|
# idempotent: a second remove-user-scope is a clean no-op (no crash on absent entry)
|
|
188
193
|
if XDG_DATA_HOME="$TMP/xdg" PI_CODING_AGENT_DIR="$AGENT_DIR" bash "$RUN" remove-user-scope >/dev/null 2>&1; then ok "run.sh remove-user-scope is idempotent (no-op second run)"; else bad "run.sh remove-user-scope second run crashed"; fi
|
|
189
194
|
|
|
195
|
+
# ── 11–13. #53 B: install must not dirty a tracked, formatter-governed settings file
|
|
196
|
+
# The cell whose ABSENCE let this ship. Every case above drove a fake settings file
|
|
197
|
+
# with absolute entries, so the one shape this repo actually commits — the portable,
|
|
198
|
+
# settings-relative `".."` that check-install-surface S7c pins — was never registered
|
|
199
|
+
# against. `setup` therefore appended the absolute path BESIDE it and rewrote the
|
|
200
|
+
# tracked, biome-owned bytes at indent=2, and `pnpm check` died at step 1 reading
|
|
201
|
+
# "format error" instead of "install wrote this".
|
|
202
|
+
#
|
|
203
|
+
# The seed is the repo's OWN committed settings, copied into a stand-in checkout so
|
|
204
|
+
# `".."` resolves to that clone rather than to the real repo (nothing here touches
|
|
205
|
+
# $REPO). Setup landing is PROVEN before any product verdict: if the seed is not the
|
|
206
|
+
# portable form, this says SETUP MISS in its own name instead of blaming the code.
|
|
207
|
+
CLONE="$TMP/selfclone"; mkdir -p "$CLONE/.pi"
|
|
208
|
+
CS="$CLONE/.pi/settings.json"
|
|
209
|
+
seed_src=""
|
|
210
|
+
if (cd "$REPO" && git show :.pi/settings.json) > "$CS" 2>/dev/null && [ -s "$CS" ]; then
|
|
211
|
+
seed_src="candidate index"
|
|
212
|
+
elif cp "$REPO/.pi/settings.json" "$CS" 2>/dev/null; then
|
|
213
|
+
seed_src="worktree"
|
|
214
|
+
fi
|
|
215
|
+
if [ ! -s "$CS" ]; then
|
|
216
|
+
bad "11 SETUP MISS: could not seed this repo's own .pi/settings.json (no git object, no worktree file)"
|
|
217
|
+
elif ! python3 -c "
|
|
218
|
+
import json,sys
|
|
219
|
+
p=json.load(open(sys.argv[1])).get('packages')
|
|
220
|
+
sys.exit(0 if p==['..'] else 1)" "$CS"; then
|
|
221
|
+
bad "11 SETUP MISS: the seed ($seed_src) is not the committed portable form (packages != ['..']) — product verdict withheld"
|
|
222
|
+
else
|
|
223
|
+
BEFORE="$(sha256sum "$CS" | cut -d' ' -f1)"; MT_B="$(stat -c %Y "$CS")"; sleep 1
|
|
224
|
+
OUT_SELF="$(python3 "$REG" "$CS" "$CLONE")"
|
|
225
|
+
AFTER="$(sha256sum "$CS" | cut -d' ' -f1)"; MT_A="$(stat -c %Y "$CS")"
|
|
226
|
+
if printf '%s' "$OUT_SELF" | grep -q 'no-op'; then
|
|
227
|
+
ok "11 register against this repo's OWN committed settings is a no-op (seed: $seed_src)"
|
|
228
|
+
else
|
|
229
|
+
bad "11 register duplicated/absolutized the portable '..' entry" "$OUT_SELF"
|
|
230
|
+
fi
|
|
231
|
+
if [ "$BEFORE" = "$AFTER" ] && [ "$MT_B" = "$MT_A" ]; then
|
|
232
|
+
ok "11b the tracked, formatter-governed bytes are UNCHANGED (sha256 + mtime)"
|
|
233
|
+
else
|
|
234
|
+
bad "11b install rewrote the tracked settings file (sha $BEFORE -> $AFTER, mtime $MT_B -> $MT_A)"
|
|
235
|
+
fi
|
|
236
|
+
# The inverse direction of the same asymmetry: the shared matcher now RECOGNIZES
|
|
237
|
+
# `".."`, so an uninstall that deleted it would edit committed source — the same
|
|
238
|
+
# defect pointed the other way. remove leaves it and says so; dry-run agrees,
|
|
239
|
+
# because both ask one predicate.
|
|
240
|
+
BEFORE_R="$(sha256sum "$CS" | cut -d' ' -f1)"
|
|
241
|
+
OUT_RM="$(python3 "$REG" "$CS" "$CLONE" --remove)"
|
|
242
|
+
OUT_DRY="$(python3 "$REG" "$CS" "$CLONE" --remove --dry-run)"
|
|
243
|
+
if [ "$BEFORE_R" = "$(sha256sum "$CS" | cut -d' ' -f1)" ]; then
|
|
244
|
+
ok "11c --remove does NOT delete the committed portable entry (bytes unchanged)"
|
|
245
|
+
else
|
|
246
|
+
bad "11c --remove edited the repo's committed settings source" "$OUT_RM"
|
|
247
|
+
fi
|
|
248
|
+
if printf '%s' "$OUT_RM" | grep -q 'kept 1 settings-relative'; then
|
|
249
|
+
ok "11d the inverse REPORTS what it deliberately left behind (never a silent partial uninstall)"
|
|
250
|
+
else
|
|
251
|
+
bad "11d --remove left the entry without saying so" "$OUT_RM"
|
|
252
|
+
fi
|
|
253
|
+
if printf '%s' "$OUT_DRY" | grep -q 'no entwurf packages\[\] entry to remove'; then
|
|
254
|
+
ok "11e --dry-run agrees with remove (one predicate, no over-report)"
|
|
255
|
+
else
|
|
256
|
+
bad "11e --dry-run disagreed with what remove actually does" "$OUT_DRY"
|
|
257
|
+
fi
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
# 12. the state a pre-fix `setup` already left on real hosts: the portable entry AND
|
|
261
|
+
# the absolute one. The repair must collapse onto the PORTABLE form — absolutizing
|
|
262
|
+
# it would fix the duplicate and dirty the tracked bytes in the same breath.
|
|
263
|
+
DUP="$TMP/dupclone"; mkdir -p "$DUP/.pi"
|
|
264
|
+
DS="$DUP/.pi/settings.json"
|
|
265
|
+
cat > "$DS" <<JSON
|
|
266
|
+
{"packages": ["..", "$DUP", "../../repos/gh/andenken"]}
|
|
267
|
+
JSON
|
|
268
|
+
python3 "$REG" "$DS" "$DUP" >/dev/null
|
|
269
|
+
if python3 -c "
|
|
270
|
+
import json
|
|
271
|
+
p=json.load(open('$DS'))['packages']
|
|
272
|
+
assert p.count('..')==1, f'the portable entry did not survive as the sole self-reference: {p}'
|
|
273
|
+
assert '$DUP' not in p, f'the absolute path was written beside/instead of the portable one: {p}'
|
|
274
|
+
assert '../../repos/gh/andenken' in p, 'unrelated relative package dropped'
|
|
275
|
+
" 2>/dev/null; then ok "12 a duplicated ('..' + absolute) settings file repairs onto the PORTABLE entry"; else bad "12 the duplicate repair absolutized or dropped the portable entry: $(cat "$DS")"; fi
|
|
276
|
+
|
|
277
|
+
# 13. a genuine rewrite keeps the file's own indent unit. Narrow by design: it does
|
|
278
|
+
# NOT make the output formatter-clean (biome also decides where short arrays
|
|
279
|
+
# collapse), which is why byte-identity for this repo's own file rests on the
|
|
280
|
+
# no-op above, not on the writer's style.
|
|
281
|
+
printf '{\n\t"packages": [\n\t\t"/old/moved/entwurf"\n\t]\n}\n' > "$S"
|
|
282
|
+
python3 "$REG" "$S" "$FAKE_REPO" >/dev/null
|
|
283
|
+
if grep -q $'^\t"packages"' "$S"; then ok "13 a rewrite preserves the file's tab indentation (never forces 2 spaces)"; else bad "13 the rewrite restyled a file it does not own: $(cat -A "$S" | head -3)"; fi
|
|
284
|
+
|
|
285
|
+
# ── 14. THE cell #53 B needed: the whole `run.sh install` drive, end to end ──
|
|
286
|
+
# Cells 11–13 call ONE python writer. That is what let the second round ship: the same
|
|
287
|
+
# settings file has TWO writers (register-pi-package for packages[], register-pi-provider
|
|
288
|
+
# for entwurfProvider.mcpServers) plus a user-scope side effect, and closing only the
|
|
289
|
+
# first left `install` still re-serializing this repo's tracked, biome-governed bytes at
|
|
290
|
+
# indent=2 — semantically a no-op, byte-wise a RED `pnpm check` diagnosed as "formatting".
|
|
291
|
+
# A per-writer cell can never see that; only the real drive can. So this one runs
|
|
292
|
+
# `run.sh install <checkout>` for real and demands sha256 + mtime invariance.
|
|
293
|
+
#
|
|
294
|
+
# The checkout is a stand-in: run.sh resolves its OWN symlinks to find REPO_DIR, so
|
|
295
|
+
# run.sh is COPIED (a link would point the drive back at the operator's real repo) while
|
|
296
|
+
# the trees it only reads are linked. That makes REPO_DIR == the stand-in, which is the
|
|
297
|
+
# shape that matters — `".."` in <checkout>/.pi/settings.json resolves to the very repo
|
|
298
|
+
# being registered, exactly as it does in a dev clone.
|
|
299
|
+
CK="$TMP/checkout"
|
|
300
|
+
mkdir -p "$CK/.pi"
|
|
301
|
+
cp "$REPO/run.sh" "$CK/run.sh"
|
|
302
|
+
for entry in scripts node_modules package.json mcp pi-extensions pi protocol.js; do
|
|
303
|
+
[ -e "$REPO/$entry" ] && ln -s "$REPO/$entry" "$CK/$entry"
|
|
304
|
+
done
|
|
305
|
+
CKS="$CK/.pi/settings.json"
|
|
306
|
+
ck_seed=""
|
|
307
|
+
if (cd "$REPO" && git show :.pi/settings.json) > "$CKS" 2>/dev/null && [ -s "$CKS" ]; then
|
|
308
|
+
ck_seed="candidate index"
|
|
309
|
+
elif cp "$REPO/.pi/settings.json" "$CKS" 2>/dev/null; then
|
|
310
|
+
ck_seed="worktree"
|
|
311
|
+
fi
|
|
312
|
+
# Landing is PROVEN before any product verdict — a bad seed or a drive that died for its
|
|
313
|
+
# own reasons is SETUP MISS, never "install dirtied the file".
|
|
314
|
+
if [ ! -s "$CKS" ]; then
|
|
315
|
+
bad "14 SETUP MISS: could not seed the stand-in checkout's .pi/settings.json"
|
|
316
|
+
elif ! python3 -c "
|
|
317
|
+
import json,sys
|
|
318
|
+
d=json.load(open(sys.argv[1]))
|
|
319
|
+
sys.exit(0 if d.get('packages')==['..'] and isinstance(d.get('entwurfProvider'),dict) else 1)" "$CKS"; then
|
|
320
|
+
bad "14 SETUP MISS: the seed ($ck_seed) is not the committed portable form (packages ['..'] + entwurfProvider)"
|
|
321
|
+
else
|
|
322
|
+
CK_BEFORE="$(sha256sum "$CKS" | cut -d' ' -f1)"; CK_MT="$(stat -c %Y "$CKS")"
|
|
323
|
+
sleep 1
|
|
324
|
+
set +e
|
|
325
|
+
# Isolated on the same line as the drive, not only by the exports above: this is a
|
|
326
|
+
# MUTATING run.sh command and it writes through three roots (agent dir, XDG state,
|
|
327
|
+
# $HOME for the user-scope registration). check-install-surface S5c matches the drive
|
|
328
|
+
# and then demands exactly these — which is why the path is unquoted here, so the
|
|
329
|
+
# tripwire can SEE the drive it is meant to police.
|
|
330
|
+
ck_out="$(HOME="$TMP/home" XDG_DATA_HOME="$TMP/xdg" XDG_STATE_HOME="$TMP/state" XDG_CACHE_HOME="$TMP/cache" PI_CODING_AGENT_DIR="$TMP/agent14" bash $CK/run.sh install "$CK" 2>&1)"
|
|
331
|
+
ck_rc=$?
|
|
332
|
+
set -e
|
|
333
|
+
if [ "$ck_rc" != 0 ]; then
|
|
334
|
+
bad "14 SETUP MISS: the stand-in \`run.sh install\` drive did not complete (exit $ck_rc) — no byte verdict from a drive that failed for its own reasons" "$ck_out"
|
|
335
|
+
else
|
|
336
|
+
ok "14 the stand-in \`run.sh install\` drive completed (seed: $ck_seed)"
|
|
337
|
+
if [ "$CK_BEFORE" = "$(sha256sum "$CKS" | cut -d' ' -f1)" ] && [ "$CK_MT" = "$(stat -c %Y "$CKS")" ]; then
|
|
338
|
+
ok "14b end-to-end: install left the tracked .pi/settings.json byte-identical (sha256 + mtime)"
|
|
339
|
+
else
|
|
340
|
+
bad "14b \`run.sh install\` rewrote the tracked settings file — one of its writers does not know the contract" "$(diff <(printf '%s' "$CK_BEFORE") <(sha256sum "$CKS" | cut -d' ' -f1); cat -A "$CKS" | head -6)"
|
|
341
|
+
fi
|
|
342
|
+
# The drive really did reach the writers (a no-op that never ran proves nothing), and
|
|
343
|
+
# the user-scope side effect landed in the sandbox rather than the operator's home.
|
|
344
|
+
case "$ck_out" in
|
|
345
|
+
*"entwurf-bridge"*) ok "14c the provider writer really ran (it reported its classification)" ;;
|
|
346
|
+
*) bad "14c the drive never reached the provider writer — 14b would be vacuous" "$ck_out" ;;
|
|
347
|
+
esac
|
|
348
|
+
if [ -f "$TMP/agent14/settings.json" ]; then
|
|
349
|
+
ok "14d the user-scope registration landed in the SANDBOX agent dir (isolation held)"
|
|
350
|
+
else
|
|
351
|
+
bad "14d the user-scope citizen was not written under the sandboxed PI_CODING_AGENT_DIR" "$ck_out"
|
|
352
|
+
fi
|
|
353
|
+
fi
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
# 15. WIRING: both writers of this one file must share the serializer, not copy it.
|
|
357
|
+
# A duplicated indent-detector is how the provider writer stayed open after the
|
|
358
|
+
# package writer was closed; a parity check is cheaper than a third round.
|
|
359
|
+
for w in register-pi-package.py register-pi-provider.py; do
|
|
360
|
+
if grep -q "from pi_settings_io import" "$REPO/scripts/$w"; then
|
|
361
|
+
ok "15 $w routes through the shared pi_settings_io serializer"
|
|
362
|
+
else
|
|
363
|
+
bad "15 $w serializes settings on its own again (copied rule = the #53 B shape)"
|
|
364
|
+
fi
|
|
365
|
+
done
|
|
366
|
+
|
|
190
367
|
echo
|
|
191
368
|
if [ "$fail" -eq 0 ]; then echo "smoke-user-scope-citizen: PASS"; else echo "smoke-user-scope-citizen: FAIL (see above)"; exit 1; fi
|