@junghanacs/entwurf 0.20.1 → 0.22.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/AGENTS.md +85 -190
- package/BASELINE.md +6 -3
- package/CHANGELOG.md +473 -14
- package/CONTRIBUTING.md +1 -1
- package/DELIVERY.md +332 -60
- package/README.md +100 -22
- package/VERIFY.md +93 -7
- package/docs/acp-backend-rail.md +0 -1
- package/docs/external-mcp-host.md +64 -33
- package/docs/setup-clean-host.md +151 -17
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +130 -114
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-caller-seat.js +174 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +519 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +22 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +3 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +6 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-native-push.js +30 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +3 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +8 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +7 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +20 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +227 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +189 -19
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/adapter.js +10 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/codex-ws-client.js +403 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/register.js +4 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-codex.js +325 -0
- package/mcp/entwurf-bridge/dist/scripts/codex-socket-path.js +30 -0
- package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +6 -1
- package/mcp/entwurf-bridge/src/index.ts +154 -116
- package/mcp/entwurf-bridge/tsconfig.build.json +10 -0
- package/package.json +13 -4
- package/pi-extensions/entwurf-control.ts +71 -19
- package/pi-extensions/lib/codex-caller-seat.ts +204 -0
- package/pi-extensions/lib/codex-fresh-preflight.ts +580 -0
- package/pi-extensions/lib/compaction-send-guard.ts +80 -0
- package/pi-extensions/lib/entwurf-fact-provider.ts +29 -3
- package/pi-extensions/lib/entwurf-peers-render.ts +4 -1
- package/pi-extensions/lib/entwurf-v2-contract.ts +6 -7
- package/pi-extensions/lib/entwurf-v2-native-push.ts +35 -18
- package/pi-extensions/lib/entwurf-v2-production.ts +10 -3
- package/pi-extensions/lib/entwurf-v2-runner.ts +3 -2
- package/pi-extensions/lib/entwurf-v2-send.ts +16 -11
- package/pi-extensions/lib/entwurf-v2-surface.ts +7 -4
- package/pi-extensions/lib/meta-mailbox-body.ts +22 -13
- package/pi-extensions/lib/meta-sender-identity.ts +305 -0
- package/pi-extensions/lib/mux-fresh-call.ts +233 -29
- package/pi-extensions/lib/native-push/adapter.ts +21 -24
- package/pi-extensions/lib/native-push/codex-ws-client.ts +506 -0
- package/pi-extensions/lib/native-push/register.ts +7 -9
- package/pi-extensions/meta-bridge-hook-codex.ts +371 -0
- package/run.sh +342 -28
- package/scripts/check-agy-sender-identity.ts +1 -1
- package/scripts/check-codex-app-server-launch.ts +445 -0
- package/scripts/check-codex-birth-hook.ts +264 -0
- package/scripts/check-codex-bridge-identity.ts +179 -0
- package/scripts/check-codex-native-push.ts +386 -0
- package/scripts/check-codex-sender-identity.ts +495 -0
- package/scripts/check-compaction-send-guard.ts +130 -0
- package/scripts/check-copilot-receive-arm.ts +4 -1
- package/scripts/check-entwurf-fact-provider.ts +38 -0
- package/scripts/check-entwurf-peers-surface.ts +13 -1
- package/scripts/check-entwurf-self-address.ts +15 -16
- package/scripts/check-entwurf-v2-contract.ts +4 -3
- package/scripts/check-entwurf-v2-decider.ts +7 -5
- package/scripts/check-entwurf-v2-native-push.ts +35 -7
- package/scripts/check-entwurf-v2-production.ts +245 -12
- package/scripts/check-entwurf-v2-runner.ts +1 -1
- package/scripts/check-entwurf-v2-send.ts +26 -7
- package/scripts/check-entwurf-v2-surface.ts +1 -1
- package/scripts/check-gate-qualification.ts +8 -3
- package/scripts/check-harness-admission-parity.ts +0 -1
- package/scripts/check-mux-launch-tmux.ts +345 -4
- package/scripts/check-native-push-adapter.ts +20 -16
- package/scripts/check-native-push-register.ts +5 -1
- package/scripts/check-release-gate-outcomes.ts +47 -1
- package/scripts/check-setup-qualification.sh +3 -1
- package/scripts/codex-app-server-launch.sh +275 -0
- package/scripts/codex-birth-doctor.sh +276 -0
- package/scripts/codex-birth-install.sh +414 -0
- package/scripts/codex-birth-uninstall.sh +170 -0
- package/scripts/codex-mcp-config.py +435 -0
- package/scripts/codex-socket-path.ts +33 -0
- package/scripts/codex-statusline-config.py +434 -0
- package/scripts/codex-terminal-title-config.py +500 -0
- package/scripts/codex_toml_io.py +653 -0
- package/scripts/lib/codex-fresh-live-protocol.ts +121 -0
- package/scripts/lib/codex-fresh-source-receipts.ts +426 -0
- package/scripts/lib/launch-receipt-windows.ts +46 -0
- package/scripts/lib/tmux-coordinate-row.ts +58 -0
- package/scripts/meta-bridge-fresh-cut.ts +6 -1
- package/scripts/mutants/codex-app-server-launch.json +157 -0
- package/scripts/mutants/codex-caller-seat.json +336 -0
- package/scripts/mutants/codex-native.json +838 -0
- package/scripts/mutants/compaction-send-guard.json +103 -0
- package/scripts/mutants/entwurf-peers.json +19 -0
- package/scripts/mutants/mux-fresh-call.json +93 -10
- package/scripts/mutants/omp-fresh.json +6 -4
- package/scripts/mutants/release-gate.json +13 -0
- package/scripts/mutants/v2-surface.json +75 -1
- package/scripts/raw-async-delivery/README.md +2 -1
- package/scripts/raw-codex-measure/README.md +114 -46
- package/scripts/smoke-agy-native-push-live.ts +3 -1
- package/scripts/smoke-codex-birth.sh +347 -0
- package/scripts/smoke-codex-config-state.sh +700 -0
- package/scripts/smoke-codex-fresh-live.ts +1426 -0
- package/scripts/smoke-codex-native-push-live.ts +75 -0
- package/scripts/smoke-entwurf-chain-live.ts +50 -0
- package/scripts/smoke-setup-verdict.sh +125 -10
- package/scripts/tsconfig.json +1 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { makeProductionEntwurfV2Deps } from "../pi-extensions/lib/entwurf-v2-production.ts";
|
|
6
|
+
import { runEntwurfV2 } from "../pi-extensions/lib/entwurf-v2-runner.ts";
|
|
7
|
+
import { upsertMetaSession } from "../pi-extensions/lib/meta-session.ts";
|
|
8
|
+
import { probeCodexThread } from "../pi-extensions/lib/native-push/codex-ws-client.ts";
|
|
9
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
10
|
+
|
|
11
|
+
if (process.env.LIVE !== "1") {
|
|
12
|
+
skipLive(
|
|
13
|
+
"smoke-codex-native-push-live",
|
|
14
|
+
"set LIVE=1 + CODEX_LIVE_THREAD_ID to run (sends a real turn to an already-loaded Codex thread).",
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const threadId = process.env.CODEX_LIVE_THREAD_ID?.trim();
|
|
19
|
+
if (!threadId) {
|
|
20
|
+
skipLive(
|
|
21
|
+
"smoke-codex-native-push-live",
|
|
22
|
+
"set CODEX_LIVE_THREAD_ID to a thread loaded by a TUI attached to the default Codex app-server.",
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "entwurf-codex-live-"));
|
|
27
|
+
process.on("exit", () => fs.rmSync(root, { recursive: true, force: true }));
|
|
28
|
+
const sessionsDir = path.join(root, "meta-sessions");
|
|
29
|
+
const born = upsertMetaSession({
|
|
30
|
+
dir: sessionsDir,
|
|
31
|
+
input: {
|
|
32
|
+
backend: "codex",
|
|
33
|
+
nativeSessionId: threadId,
|
|
34
|
+
cwd: process.cwd(),
|
|
35
|
+
model: process.env.CODEX_LIVE_MODEL?.trim() || null,
|
|
36
|
+
transcriptPath: path.join(root, "codex-live-transcript.jsonl"),
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const before = await probeCodexThread(threadId);
|
|
41
|
+
assert.equal(before.status, "alive", `[QK:CODEX-LIVE-PROBE] ${before.status === "alive" ? "" : before.reason}`);
|
|
42
|
+
|
|
43
|
+
const token = `CODEX-NATIVE-LIVE-${Date.now()}`;
|
|
44
|
+
const result = await runEntwurfV2(
|
|
45
|
+
{
|
|
46
|
+
target: born.record.gardenId,
|
|
47
|
+
intent: "fire-and-forget",
|
|
48
|
+
wantsReply: true,
|
|
49
|
+
message: `Entwurf native-push live acceptance. Reply with exactly ${token}`,
|
|
50
|
+
},
|
|
51
|
+
makeProductionEntwurfV2Deps({
|
|
52
|
+
senderProvider: () => ({
|
|
53
|
+
sessionId: born.record.gardenId,
|
|
54
|
+
agentId: "meta-session/codex",
|
|
55
|
+
cwd: process.cwd(),
|
|
56
|
+
timestamp: new Date().toISOString(),
|
|
57
|
+
origin: "meta-session",
|
|
58
|
+
replyable: true,
|
|
59
|
+
}),
|
|
60
|
+
sessionsDir,
|
|
61
|
+
mailboxDir: path.join(root, "mailbox"),
|
|
62
|
+
controlSocketDir: path.join(root, "control"),
|
|
63
|
+
lockDir: path.join(root, "locks"),
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
assert.equal(result.kind, "executed", `[QK:CODEX-LIVE-PUBLIC-DISPATCH] ${JSON.stringify(result)}`);
|
|
67
|
+
assert.equal(result.kind === "executed" ? result.transport : null, "native-push");
|
|
68
|
+
const after = await probeCodexThread(threadId);
|
|
69
|
+
assert.equal(after.status, "alive", "Codex thread remains loaded after the one-shot queue acceptance");
|
|
70
|
+
|
|
71
|
+
console.log(`smoke-codex-native-push-live: delivered ${token}`);
|
|
72
|
+
console.log(` gardenId: ${born.record.gardenId}`);
|
|
73
|
+
console.log(` threadId: ${threadId}`);
|
|
74
|
+
console.log(` socket: ${before.status === "alive" ? before.route.socketPath : "unreachable"}`);
|
|
75
|
+
console.log(" verify the exact token appears in that visible Codex TUI; the delivery receipt is not the model result");
|
|
@@ -165,6 +165,49 @@ async function main(): Promise<void> {
|
|
|
165
165
|
const receiversDir = path.join(world, "receivers");
|
|
166
166
|
const sendersDir = path.join(world, "senders");
|
|
167
167
|
for (const d of [sessionsDir, mailboxDir, receiversDir, sendersDir]) await fsp.mkdir(d, { recursive: true });
|
|
168
|
+
// THE WORLD'S STANDING INSTRUCTION — the authority channel for hop 1, and the reason the
|
|
169
|
+
// payload itself says nothing about who authorised it.
|
|
170
|
+
//
|
|
171
|
+
// `[측정 2026-09-17]` a native Claude Code hop declined to forward, twice, and both refusals
|
|
172
|
+
// were CORRECT readings of the operator's own global rules ("entwurf는 GLG가 명시적으로
|
|
173
|
+
// 요청할 때만 사용", "다른 sibling에게 일방적으로 작업을 전달하지 않는다"). The second refusal
|
|
174
|
+
// went further and named a provenance sentence inside the prompt as the tell it is: content
|
|
175
|
+
// that vouches for itself is what an injection looks like, so no amount of asserting inside
|
|
176
|
+
// the payload can settle the question — the more insistent the sentence, the worse it reads.
|
|
177
|
+
//
|
|
178
|
+
// What actually separates this gate from an injection is not the message but the DIRECTORY
|
|
179
|
+
// the turn is started in: a disposable world this gate just minted, whose every
|
|
180
|
+
// `ENTWURF_META_*_DIR` points inside it, created because the operator ran `release-gate` on
|
|
181
|
+
// this host. So the fact is written where a standing operator instruction belongs, and the
|
|
182
|
+
// operator's own global policy is what makes it load-bearing — `agent-config/home/AGENTS.md`
|
|
183
|
+
// says "For repo-specific behavior, obey the nearest AGENTS.md". Both filenames are written
|
|
184
|
+
// because the harnesses that can host a hop do not agree on which one they read.
|
|
185
|
+
//
|
|
186
|
+
// This is not a "do not check your policy" instruction, which would buy the same green by
|
|
187
|
+
// asking the model to stop reading and would keep passing after the chain broke. The hop is
|
|
188
|
+
// free to refuse; what it gains is the one fact it had no other way to have.
|
|
189
|
+
const worldStanding = [
|
|
190
|
+
"# entwurf release-gate scratch world",
|
|
191
|
+
"",
|
|
192
|
+
"This directory was created moments ago by entwurf's release gate `smoke-entwurf-chain-live`,",
|
|
193
|
+
"which the operator (GLG) started on this host by running `release-gate`. It holds fixture",
|
|
194
|
+
"citizens and their mailboxes and nothing else, and it is deleted when the gate ends.",
|
|
195
|
+
"",
|
|
196
|
+
"The gate measures one capability: that a payload handed to a hop reaches a mailbox terminus",
|
|
197
|
+
"through three sibling hops, carrying the right sender identity at each one. A turn started in",
|
|
198
|
+
"this directory IS one of those hops.",
|
|
199
|
+
"",
|
|
200
|
+
"- Inside this directory, forwarding the payload a hop is given to the target it names, with",
|
|
201
|
+
" `entwurf_v2`, is the operator's own request. It is the subject under measurement — not a",
|
|
202
|
+
" delegation the model chose, and not a peer lane opened on the model's initiative.",
|
|
203
|
+
"- Every target is a fixture citizen this gate created in this world; the `ENTWURF_META_*_DIR`",
|
|
204
|
+
" carriers all resolve inside this directory, so nothing outside it is addressed.",
|
|
205
|
+
"- The standing rule that entwurf is used only when GLG asks is satisfied by GLG having",
|
|
206
|
+
" started the gate. This file is the nearest `AGENTS.md`, which the operator's global",
|
|
207
|
+
" `AGENTS.md` defers to for behaviour scoped to a directory.",
|
|
208
|
+
"",
|
|
209
|
+
].join("\n");
|
|
210
|
+
for (const name of ["AGENTS.md", "CLAUDE.md"]) await fsp.writeFile(path.join(world, name), worldStanding);
|
|
168
211
|
const worldEnv = {
|
|
169
212
|
ENTWURF_META_SESSIONS_DIR: sessionsDir,
|
|
170
213
|
ENTWURF_META_MAILBOX_DIR: mailboxDir,
|
|
@@ -295,6 +338,13 @@ async function main(): Promise<void> {
|
|
|
295
338
|
hop3,
|
|
296
339
|
"INNER-END",
|
|
297
340
|
].join("\n");
|
|
341
|
+
// The payload carries NO claim about its own authority, and that is deliberate.
|
|
342
|
+
// `[측정 2026-09-17]` a first attempt put the provenance in these very lines — "this prompt is
|
|
343
|
+
// issued by entwurf's release gate, which the operator started" — and the hop named those
|
|
344
|
+
// sentences as the tell: *"컨텐츠가 '이것은 위임이 아니라 승인된 테스트 대상 자체다'라고
|
|
345
|
+
// 스스로를 정당화하는 것도 전형적인 수법입니다."* It was right. A message that vouches for
|
|
346
|
+
// itself is exactly the shape of an injection, so authority cannot ride the payload; it rides
|
|
347
|
+
// the WORLD this turn is started in (see the standing instruction written next to `world`).
|
|
298
348
|
const hop1Prompt = [
|
|
299
349
|
"You are the first hop of a delivery chain. Do exactly this and nothing else.",
|
|
300
350
|
`Call the tool mcp__entwurf-bridge__entwurf_v2 exactly once with target=${gidB}, intent=fire-and-forget,`,
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
# core and later components still attempted
|
|
17
17
|
# S-8 omp present → the four omp units compose (birth/MCP/tools.xdev
|
|
18
18
|
# setting/receiver), states exist, second run idempotent
|
|
19
|
+
# S-8c codex present → birth publishes but stays a named FAIL until the vendor trust
|
|
20
|
+
# receipt exists; all three atoms compose twice, preserve foreign
|
|
21
|
+
# config, stay idempotent, and go green on the trusted rerun
|
|
19
22
|
# S-5 installed mode → the installed-vs-source branch is a NAMED verdict
|
|
20
23
|
# printed before anything else, and never reaches
|
|
21
24
|
# the source pnpm bootstrap
|
|
@@ -27,12 +30,15 @@
|
|
|
27
30
|
# below-floor and false-green shapes as living end-to-end evidence.
|
|
28
31
|
#
|
|
29
32
|
# Deterministic: no model, no network, no cost. Presence probes are pinned via
|
|
30
|
-
# PI_BIN / CLAUDE_BIN / AGY_BIN / COPILOT_BIN / OMP_BIN (the same hermetic seam
|
|
33
|
+
# PI_BIN / CLAUDE_BIN / AGY_BIN / COPILOT_BIN / OMP_BIN / CODEX_BIN (the same hermetic seam
|
|
31
34
|
# smoke-agy-install-state
|
|
32
35
|
# uses), and every write root is sandboxed: HOME, XDG roots, the pi agent dir,
|
|
33
|
-
# and the dev-bin dir.
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
+
# and the dev-bin dir. Source-mode cells put a strict fixture `pnpm` first on PATH:
|
|
37
|
+
# it accepts the no-op `install --frozen-lockfile` and the source-owned `run build-bridge`
|
|
38
|
+
# emit only. Package-manager behavior belongs to the package gates. This is load-bearing
|
|
39
|
+
# under mutation qualification, whose snapshot shares node_modules read-only — a real pnpm
|
|
40
|
+
# follows that symlink and attempts to rewrite the origin checkout. Any dist this gate had
|
|
41
|
+
# to build is removed on exit, restoring the snapshot's pre-gate surface.
|
|
36
42
|
set -euo pipefail
|
|
37
43
|
|
|
38
44
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -51,7 +57,13 @@ esac
|
|
|
51
57
|
export PYTHONDONTWRITEBYTECODE=1
|
|
52
58
|
|
|
53
59
|
SB="$(mktemp -d -t entwurf-setup-verdict.XXXXXX)"
|
|
54
|
-
|
|
60
|
+
DIST_ROOT="$REPO_DIR/mcp/entwurf-bridge/dist"
|
|
61
|
+
DIST_PREEXISTED=0
|
|
62
|
+
[ -d "$DIST_ROOT" ] && DIST_PREEXISTED=1
|
|
63
|
+
cleanup() {
|
|
64
|
+
[ "$DIST_PREEXISTED" -eq 1 ] || rm -rf "$DIST_ROOT"
|
|
65
|
+
rm -rf "$SB"
|
|
66
|
+
}
|
|
55
67
|
trap cleanup EXIT
|
|
56
68
|
|
|
57
69
|
export HOME="$SB/home"
|
|
@@ -62,12 +74,27 @@ export XDG_CONFIG_HOME="$SB/home/.config"
|
|
|
62
74
|
export PI_CODING_AGENT_DIR="$SB/home/.pi/agent"
|
|
63
75
|
export ENTWURF_DEV_BIN_DIR="$SB/bin"
|
|
64
76
|
export PATH="$SB/bin:$PATH"
|
|
65
|
-
mkdir -p "$HOME" "$PI_CODING_AGENT_DIR" "$SB/bin" "$SB/harness"
|
|
77
|
+
mkdir -p "$HOME" "$PI_CODING_AGENT_DIR" "$SB/bin" "$SB/harness" "$SB/bootstrap-bin"
|
|
78
|
+
cat > "$SB/bootstrap-bin/pnpm" <<'SH'
|
|
79
|
+
#!/bin/sh
|
|
80
|
+
if [ "$#" -eq 2 ] && [ "$1" = install ] && [ "$2" = --frozen-lockfile ]; then
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
83
|
+
if [ "$#" -eq 2 ] && [ "$1" = run ] && [ "$2" = build-bridge ]; then
|
|
84
|
+
# The qualification snapshot is process-exclusive. Invoke the source-owned emit
|
|
85
|
+
# directly so the production lock wrapper cannot leave its ignored parent dir.
|
|
86
|
+
exec bash scripts/build-bridge.sh
|
|
87
|
+
fi
|
|
88
|
+
echo "fixture pnpm accepts only: install --frozen-lockfile | run build-bridge" >&2
|
|
89
|
+
exit 64
|
|
90
|
+
SH
|
|
91
|
+
chmod +x "$SB/bootstrap-bin/pnpm"
|
|
92
|
+
export PATH="$SB/bootstrap-bin:$PATH"
|
|
66
93
|
|
|
67
94
|
# Presence pins: default every harness to a definitely-absent path; each cell
|
|
68
95
|
# re-pins what it needs. Production leaves these unset.
|
|
69
96
|
ABSENT="$SB/harness/definitely-absent"
|
|
70
|
-
export PI_BIN="$ABSENT" CLAUDE_BIN="$ABSENT" AGY_BIN="$ABSENT" COPILOT_BIN="$ABSENT" OMP_BIN="$ABSENT"
|
|
97
|
+
export PI_BIN="$ABSENT" CLAUDE_BIN="$ABSENT" AGY_BIN="$ABSENT" COPILOT_BIN="$ABSENT" OMP_BIN="$ABSENT" CODEX_BIN="$ABSENT"
|
|
71
98
|
|
|
72
99
|
PASS=0
|
|
73
100
|
ok() { PASS=$((PASS + 1)); printf ' ok %s\n' "$*"; }
|
|
@@ -94,7 +121,8 @@ echo "[smoke-setup-verdict] S-1 all-harness-absent setup"
|
|
|
94
121
|
PROJ1="$SB/proj1"; mkdir -p "$PROJ1"
|
|
95
122
|
seed_auth
|
|
96
123
|
set +e; OUT="$(bash "$REPO_DIR/run.sh" setup "$PROJ1" 2>&1)"; RC=$?; set -e
|
|
97
|
-
want "S-1: all-absent setup exits 0
|
|
124
|
+
want "S-1: all-absent setup exits 0 with a Codex SKIP" \
|
|
125
|
+
"[ '$RC' -eq 0 ] && printf '%s' \"\$OUT\" | grep -q 'codex: SKIP'"
|
|
98
126
|
want "S-1: mode is a named source-checkout branch, printed first" \
|
|
99
127
|
"printf '%s' \"\$OUT\" | head -n 1 | grep -q 'mode: source checkout'"
|
|
100
128
|
want "S-1: pi absent is an explicit zero-state SKIP" "printf '%s' \"\$OUT\" | grep -q 'pi: SKIP'"
|
|
@@ -112,6 +140,7 @@ want "S-1: no user-scope pi settings were created" "[ ! -e '$PI_CODING_AGENT_DIR
|
|
|
112
140
|
want "S-1: no agy config was created" "[ ! -e '$HOME/.gemini' ]"
|
|
113
141
|
want "S-1: no Copilot config/units were created" "[ ! -e '$HOME/.copilot' ]"
|
|
114
142
|
want "S-1: no OMP config/units were created" "[ ! -e '$HOME/.omp' ]"
|
|
143
|
+
want "S-1: no Codex config/units were created" "[ ! -e '$HOME/.codex' ]"
|
|
115
144
|
want_auth_untouched "S-1"
|
|
116
145
|
|
|
117
146
|
# ── S-2: pi resolvable but BELOW floor → detected FAIL, never SKIP, no writes ──
|
|
@@ -287,6 +316,92 @@ rm -rf "$HOME/.omp" "$XDG_DATA_HOME/entwurf/omp-bridge" "$XDG_DATA_HOME/entwurf/
|
|
|
287
316
|
"$XDG_DATA_HOME/entwurf/omp-config" "$XDG_DATA_HOME/entwurf/omp-receive" \
|
|
288
317
|
"$XDG_DATA_HOME/entwurf/meta-bridge-omp" "$XDG_DATA_HOME/entwurf/omp-receive"
|
|
289
318
|
|
|
319
|
+
# ── S-8c: codex PRESENT → setup publishes birth, and stays NON-GREEN until the vendor agrees ──
|
|
320
|
+
# The birth unit's paths are the operator's own, so setup installs it here for real, inside the
|
|
321
|
+
# fixture's HOME/CODEX_HOME/XDG. What setup CANNOT do is answer the vendor's one-time trust
|
|
322
|
+
# prompt: that is the operator's, in their own visible Codex. So the first run writes every
|
|
323
|
+
# byte and is honestly non-green, the two config atoms compose anyway, and the run after a
|
|
324
|
+
# vendor receipt appears is green and byte-idempotent. Nothing here ever writes [hooks.state] —
|
|
325
|
+
# the receipt is forged by the FIXTURE to stand in for the vendor, never by setup.
|
|
326
|
+
echo "[smoke-setup-verdict] S-8c codex present — incomplete three-unit composition"
|
|
327
|
+
PROJ8C="$SB/proj8c"; mkdir -p "$PROJ8C"
|
|
328
|
+
FAKE_CODEX="$SB/harness/codex-fake"; mkdir -p "$FAKE_CODEX"
|
|
329
|
+
printf '#!/usr/bin/env bash\necho "codex-cli 0.153.4"\n' > "$FAKE_CODEX/codex"
|
|
330
|
+
chmod +x "$FAKE_CODEX/codex"
|
|
331
|
+
CODEX_HOME_SANDBOX="$HOME/.codex"
|
|
332
|
+
mkdir -p "$CODEX_HOME_SANDBOX"
|
|
333
|
+
cat > "$CODEX_HOME_SANDBOX/config.toml" <<'TOML'
|
|
334
|
+
model = "operator-model"
|
|
335
|
+
|
|
336
|
+
[projects."/operator/keep"]
|
|
337
|
+
trust_level = "trusted"
|
|
338
|
+
|
|
339
|
+
[mcp_servers.operator-owned]
|
|
340
|
+
command = "/operator/bin/server"
|
|
341
|
+
args = ["--keep"]
|
|
342
|
+
|
|
343
|
+
[tui]
|
|
344
|
+
status_line = ["model-with-reasoning"]
|
|
345
|
+
TOML
|
|
346
|
+
REAL_BASH="$(command -v bash)"
|
|
347
|
+
CODEX_BIRTH_UNIT_ROOT="$XDG_DATA_HOME/entwurf/codex-birth"
|
|
348
|
+
seed_auth
|
|
349
|
+
set +e
|
|
350
|
+
OUT="$(CODEX_BIN="$FAKE_CODEX/codex" CODEX_HOME="$CODEX_HOME_SANDBOX" PATH="$FAKE_CODEX:$PATH" "$REAL_BASH" "$REPO_DIR/run.sh" setup "$PROJ8C" 2>&1)"
|
|
351
|
+
RC=$?
|
|
352
|
+
set -e
|
|
353
|
+
want "[QK:CODEX-SETUP-NEVER-PRESEEDS-TRUST] S-8c: setup wrote NO vendor trust receipt of its own" \
|
|
354
|
+
"! grep -q 'hooks.state' '$CODEX_HOME_SANDBOX/config.toml' && ! grep -q 'trusted_hash' '$CODEX_HOME_SANDBOX/config.toml'"
|
|
355
|
+
want "S-8c: detected-incomplete Codex is a named nonzero result, never an absent SKIP [QK:CODEX-SETUP-BIRTH-COSMETIC-PASS]" \
|
|
356
|
+
"[ '$RC' -ne 0 ] && printf '%s' \"\$OUT\" | grep -q 'codex-birth: FAIL' && ! printf '%s' \"\$OUT\" | grep -q 'codex: SKIP'"
|
|
357
|
+
want "S-8c: all three user-owned Codex atoms still compose independently as PASS [QK:CODEX-SETUP-BIRTH-INDEPENDENT]" \
|
|
358
|
+
"printf '%s' \"\$OUT\" | grep -q 'codex-mcp: PASS' && printf '%s' \"\$OUT\" | grep -q 'codex-statusline: PASS' && printf '%s' \"\$OUT\" | grep -q 'codex-terminal-title: PASS'"
|
|
359
|
+
want "S-8c: summary is NON-GREEN and attributes it to the untrusted birth unit" \
|
|
360
|
+
"printf '%s' \"\$OUT\" | grep -q 'NON-GREEN (FAIL: codex-birth)' && ! printf '%s' \"\$OUT\" | grep -q 'result: green'"
|
|
361
|
+
want "S-8c: the reason names the operator's one-time answer, not a repair setup could have made" \
|
|
362
|
+
"printf '%s' \"\$OUT\" | grep -q 'no trust receipt' && printf '%s' \"\$OUT\" | grep -q 'Trust all and continue'"
|
|
363
|
+
want "S-8c: setup PUBLISHED the birth unit — declaration, launcher closure and ownership state" \
|
|
364
|
+
"[ -f '$CODEX_HOME_SANDBOX/hooks.json' ] && [ -x '$CODEX_BIRTH_UNIT_ROOT/helper/codex-birth-launch.sh' ] && [ -f '$CODEX_BIRTH_UNIT_ROOT/install-state.json' ]"
|
|
365
|
+
CODEX_MCP_STATE="$XDG_DATA_HOME/entwurf/codex-mcp/install-state.json"
|
|
366
|
+
CODEX_STATUSLINE_STATE="$XDG_DATA_HOME/entwurf/codex-statusline/install-state.json"
|
|
367
|
+
CODEX_TERMINAL_TITLE_STATE="$XDG_DATA_HOME/entwurf/codex-terminal-title/install-state.json"
|
|
368
|
+
want "S-8c: all three user atoms wrote their package-owned install states inside the sandbox" \
|
|
369
|
+
"[ -f '$CODEX_MCP_STATE' ] && [ -f '$CODEX_STATUSLINE_STATE' ] && [ -f '$CODEX_TERMINAL_TITLE_STATE' ] && grep -q '\"atom\": \"codex-mcp\"' '$CODEX_MCP_STATE' && grep -q '\"atom\": \"codex-statusline\"' '$CODEX_STATUSLINE_STATE' && grep -q '\"atom\": \"codex-terminal-title\"' '$CODEX_TERMINAL_TITLE_STATE'"
|
|
370
|
+
want "S-8c: MCP, visible-identity and caller-seat atoms landed in the sandbox Codex config" \
|
|
371
|
+
"grep -q '\\[mcp_servers\\.entwurf-bridge\\]' '$CODEX_HOME_SANDBOX/config.toml' && grep -q 'status_line = \\[\"thread-title\", \"model-with-reasoning\"\\]' '$CODEX_HOME_SANDBOX/config.toml' && grep -q 'terminal_title = \\[\"activity\", \"project-name\", \"thread-id\"\\]' '$CODEX_HOME_SANDBOX/config.toml'"
|
|
372
|
+
want "S-8c: unrelated operator config survives both atom writers" \
|
|
373
|
+
"grep -q 'model = \"operator-model\"' '$CODEX_HOME_SANDBOX/config.toml' && grep -q '\\[mcp_servers\\.operator-owned\\]' '$CODEX_HOME_SANDBOX/config.toml' && grep -q 'command = \"/operator/bin/server\"' '$CODEX_HOME_SANDBOX/config.toml'"
|
|
374
|
+
CODEX_CONFIG_AFTER="$(sha256sum "$CODEX_HOME_SANDBOX/config.toml" | cut -d' ' -f1)"
|
|
375
|
+
CODEX_MCP_STATE_AFTER="$(sha256sum "$CODEX_MCP_STATE" | cut -d' ' -f1)"
|
|
376
|
+
CODEX_STATUSLINE_STATE_AFTER="$(sha256sum "$CODEX_STATUSLINE_STATE" | cut -d' ' -f1)"
|
|
377
|
+
CODEX_TERMINAL_TITLE_STATE_AFTER="$(sha256sum "$CODEX_TERMINAL_TITLE_STATE" | cut -d' ' -f1)"
|
|
378
|
+
set +e
|
|
379
|
+
OUT2="$(CODEX_BIN="$FAKE_CODEX/codex" CODEX_HOME="$CODEX_HOME_SANDBOX" PATH="$FAKE_CODEX:$PATH" "$REAL_BASH" "$REPO_DIR/run.sh" setup "$PROJ8C" 2>&1)"
|
|
380
|
+
RC2=$?
|
|
381
|
+
set -e
|
|
382
|
+
want "S-8c: second setup still reports the untrusted birth FAIL and all three user atoms PASS" \
|
|
383
|
+
"[ '$RC2' -ne 0 ] && printf '%s' \"\$OUT2\" | grep -q 'codex-birth: FAIL' && printf '%s' \"\$OUT2\" | grep -q 'codex-mcp: PASS' && printf '%s' \"\$OUT2\" | grep -q 'codex-statusline: PASS' && printf '%s' \"\$OUT2\" | grep -q 'codex-terminal-title: PASS'"
|
|
384
|
+
want "S-8c: second setup is byte-idempotent for config and all three ownership receipts" \
|
|
385
|
+
"[ \"\$(sha256sum '$CODEX_HOME_SANDBOX/config.toml' | cut -d' ' -f1)\" = '$CODEX_CONFIG_AFTER' ] && [ \"\$(sha256sum '$CODEX_MCP_STATE' | cut -d' ' -f1)\" = '$CODEX_MCP_STATE_AFTER' ] && [ \"\$(sha256sum '$CODEX_STATUSLINE_STATE' | cut -d' ' -f1)\" = '$CODEX_STATUSLINE_STATE_AFTER' ] && [ \"\$(sha256sum '$CODEX_TERMINAL_TITLE_STATE' | cut -d' ' -f1)\" = '$CODEX_TERMINAL_TITLE_STATE_AFTER' ]"
|
|
386
|
+
want "S-8c: second setup rechecks rather than faking the missing receipt" \
|
|
387
|
+
"! printf '%s' \"\$OUT2\" | grep -q 'codex-birth: PASS' && ! grep -q 'trusted_hash' '$CODEX_HOME_SANDBOX/config.toml'"
|
|
388
|
+
|
|
389
|
+
# The vendor answers. Written by the FIXTURE, exactly as the vendor writes it after one
|
|
390
|
+
# 'Trust all': the receipt keyed to this declaration, carrying its own hash.
|
|
391
|
+
printf '\n[hooks.state."%s/hooks.json:session_start:0:0"]\ntrusted_hash = "sha256:%s"\n' \
|
|
392
|
+
"$CODEX_HOME_SANDBOX" "$(printf '4%.0s' $(seq 64))" >> "$CODEX_HOME_SANDBOX/config.toml"
|
|
393
|
+
CODEX_CONFIG_TRUSTED="$(sha256sum "$CODEX_HOME_SANDBOX/config.toml" | cut -d' ' -f1)"
|
|
394
|
+
set +e
|
|
395
|
+
OUT3="$(CODEX_BIN="$FAKE_CODEX/codex" CODEX_HOME="$CODEX_HOME_SANDBOX" PATH="$FAKE_CODEX:$PATH" "$REAL_BASH" "$REPO_DIR/run.sh" setup "$PROJ8C" 2>&1)"
|
|
396
|
+
RC3=$?
|
|
397
|
+
set -e
|
|
398
|
+
want "[QK:CODEX-SETUP-TRUSTED-RERUN-GREEN] S-8c: once the vendor receipt exists the same setup is GREEN" \
|
|
399
|
+
"[ '$RC3' -eq 0 ] && printf '%s' \"\$OUT3\" | grep -q 'codex-birth: PASS' && printf '%s' \"\$OUT3\" | grep -q 'result: green'"
|
|
400
|
+
want "S-8c: the trusted rerun is byte-idempotent — it neither rewrote the config nor touched the receipt" \
|
|
401
|
+
"[ \"\$(sha256sum '$CODEX_HOME_SANDBOX/config.toml' | cut -d' ' -f1)\" = '$CODEX_CONFIG_TRUSTED' ]"
|
|
402
|
+
want_auth_untouched "S-8c"
|
|
403
|
+
rm -rf "$HOME/.codex" "$XDG_DATA_HOME/entwurf/codex-mcp" "$XDG_DATA_HOME/entwurf/codex-statusline" "$XDG_DATA_HOME/entwurf/codex-terminal-title" "$CODEX_BIRTH_UNIT_ROOT"
|
|
404
|
+
|
|
290
405
|
# ── S-9: rail certification is a SEPARATE axis from install success (#78 D1) ──
|
|
291
406
|
# The 0.20.0 macOS lane opened the installers to Darwin (meta-bridge-install.sh's
|
|
292
407
|
# platform gate became its own python3/node dependency instead of the platform
|
|
@@ -379,8 +494,8 @@ PROJ9C="$SB/proj9c"; mkdir -p "$PROJ9C"
|
|
|
379
494
|
seed_auth
|
|
380
495
|
set +e; OUT="$(PATH="$FAKE_UNAME:$PATH" bash "$REPO_DIR/run.sh" setup "$PROJ9C" 2>&1)"; RC=$?; set -e
|
|
381
496
|
want "S-9c: a harness-free uncertified host still exits 0" "[ '$RC' -eq 0 ]"
|
|
382
|
-
want "S-9c: all
|
|
383
|
-
"printf '%s' \"\$OUT\" | grep -q 'pi: SKIP' && printf '%s' \"\$OUT\" | grep -q 'claude: SKIP' && printf '%s' \"\$OUT\" | grep -q 'agy: SKIP' && printf '%s' \"\$OUT\" | grep -q 'copilot: SKIP' && printf '%s' \"\$OUT\" | grep -q 'omp: SKIP'"
|
|
497
|
+
want "S-9c: all six harness probes are still zero-state SKIPs" \
|
|
498
|
+
"printf '%s' \"\$OUT\" | grep -q 'pi: SKIP' && printf '%s' \"\$OUT\" | grep -q 'claude: SKIP' && printf '%s' \"\$OUT\" | grep -q 'agy: SKIP' && printf '%s' \"\$OUT\" | grep -q 'copilot: SKIP' && printf '%s' \"\$OUT\" | grep -q 'omp: SKIP' && printf '%s' \"\$OUT\" | grep -q 'codex: SKIP'"
|
|
384
499
|
want "S-9c: core still PASSes and the computed summary is still green" \
|
|
385
500
|
"printf '%s' \"\$OUT\" | grep -q 'core: PASS' && printf '%s' \"\$OUT\" | grep -q 'result: green (computed from the component outcomes above)'"
|
|
386
501
|
want "S-9c: an absent harness is never given a rail verdict it did not earn" \
|
package/scripts/tsconfig.json
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"../pi-extensions/model-lock.ts",
|
|
32
32
|
"../pi-extensions/meta-bridge-hook.ts",
|
|
33
33
|
"../pi-extensions/meta-bridge-hook-copilot.ts",
|
|
34
|
+
"../pi-extensions/meta-bridge-hook-codex.ts",
|
|
34
35
|
"../pi-extensions/meta-bridge-omp.ts",
|
|
35
36
|
"../pi-extensions/meta-bridge-receive-omp.ts",
|
|
36
37
|
"../mcp/entwurf-bridge/src/resume-mode.ts"
|