@junghanacs/entwurf 0.20.0 → 0.21.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 -5
- package/CHANGELOG.md +220 -14
- package/CONTRIBUTING.md +1 -1
- package/DELIVERY.md +202 -60
- package/README.md +54 -26
- package/VERIFY.md +37 -7
- package/docs/acp-backend-rail.md +31 -15
- package/docs/external-mcp-host.md +58 -35
- package/docs/fresh-cut-policy.md +3 -2
- package/docs/setup-clean-host.md +110 -17
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +101 -109
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +6 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +326 -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-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 +50 -15
- 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/meta-bridge-fresh-cut.js +6 -1
- package/mcp/entwurf-bridge/src/index.ts +115 -111
- package/mcp/entwurf-bridge/tsconfig.build.json +1 -0
- package/package.json +14 -5
- package/pi-extensions/entwurf-control.ts +71 -19
- package/pi-extensions/lib/acp/acp-client.ts +3 -3
- package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
- package/pi-extensions/lib/acp/backend.ts +3 -3
- package/pi-extensions/lib/acp/event-mapper.ts +4 -4
- package/pi-extensions/lib/acp/tool-surface.ts +6 -0
- package/pi-extensions/lib/codex-fresh-preflight.ts +363 -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-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 +64 -19
- 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 +251 -35
- package/scripts/check-acp-usage-accounting.ts +9 -9
- package/scripts/check-agy-sender-identity.ts +1 -1
- 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 +203 -11
- package/scripts/check-entwurf-v2-runner.ts +1 -1
- package/scripts/check-entwurf-v2-surface.ts +1 -1
- package/scripts/check-gate-qualification.ts +7 -4
- package/scripts/check-harness-admission-parity.ts +0 -1
- package/scripts/check-install-surface.ts +23 -7
- package/scripts/check-mux-launch-tmux.ts +47 -2
- 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-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-statusline-config.py +434 -0
- package/scripts/codex_toml_io.py +532 -0
- package/scripts/lib/codex-fresh-live-protocol.ts +113 -0
- package/scripts/lib/codex-fresh-source-receipts.ts +399 -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-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 +19 -8
- package/scripts/mutants/omp-fresh.json +6 -4
- package/scripts/mutants/pi-package-ownership.json +26 -0
- package/scripts/mutants/release-gate.json +13 -0
- package/scripts/mutants/v2-surface.json +53 -1
- package/scripts/pi_settings_io.py +3 -1
- package/scripts/raw-async-delivery/README.md +2 -1
- package/scripts/raw-codex-measure/README.md +114 -46
- package/scripts/register-pi-package.py +38 -30
- package/scripts/register-pi-provider.py +3 -2
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- 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 +511 -0
- package/scripts/smoke-codex-fresh-live.ts +1186 -0
- package/scripts/smoke-codex-native-push-live.ts +75 -0
- package/scripts/smoke-setup-verdict.sh +123 -10
- package/scripts/smoke-user-scope-citizen.sh +199 -71
- package/scripts/tsconfig.json +1 -0
|
@@ -31,7 +31,7 @@ import * as path from "node:path";
|
|
|
31
31
|
import { fileURLToPath } from "node:url";
|
|
32
32
|
import { type EntwurfFactsResult, recordLessSocketMessage } from "../pi-extensions/lib/entwurf-fact-provider.ts";
|
|
33
33
|
import type { PeerFact } from "../pi-extensions/lib/entwurf-facts.ts";
|
|
34
|
-
import { renderEntwurfPeers } from "../pi-extensions/lib/entwurf-peers-render.ts";
|
|
34
|
+
import { ENTWURF_PEERS_RENDER_LIMIT, renderEntwurfPeers } from "../pi-extensions/lib/entwurf-peers-render.ts";
|
|
35
35
|
import type { FactLiveness } from "../pi-extensions/lib/entwurf-v2-contract.ts";
|
|
36
36
|
import type { MetaCitizenBackend } from "../pi-extensions/lib/meta-session.ts";
|
|
37
37
|
import type { SocketLiveness } from "../pi-extensions/lib/socket-probe.ts";
|
|
@@ -199,6 +199,10 @@ function main(): void {
|
|
|
199
199
|
);
|
|
200
200
|
const bounded = renderEntwurfPeers({ facts: { peers: manyPeers, recordLessSockets: [] }, diagnostics: [] });
|
|
201
201
|
ok("bounded text omits older entries when peer list is large", bounded.text.includes("older entries omitted"));
|
|
202
|
+
ok(
|
|
203
|
+
"#112: human row budget remains exactly 32",
|
|
204
|
+
ENTWURF_PEERS_RENDER_LIMIT === 32 && bounded.text.includes("(8 older entries omitted; showing latest 32)"),
|
|
205
|
+
);
|
|
202
206
|
ok("bounded text shows latest entries", bounded.text.includes("20260612T000039-aaaaaa"));
|
|
203
207
|
ok("bounded text omits oldest entry", !bounded.text.includes("20260612T000000-aaaaaa backend="));
|
|
204
208
|
ok("bounded payload still carries every peer", bounded.payload.peers.length === 40);
|
|
@@ -255,6 +259,14 @@ function main(): void {
|
|
|
255
259
|
ok("wiring: bridge calls renderEntwurfPeers(", bridgeSrc.includes("renderEntwurfPeers("));
|
|
256
260
|
ok("wiring: native pi tool calls listEntwurfFacts(", nativeSrc.includes("listEntwurfFacts("));
|
|
257
261
|
ok("wiring: native pi tool calls renderEntwurfPeers(", nativeSrc.includes("renderEntwurfPeers("));
|
|
262
|
+
ok(
|
|
263
|
+
"#112 wiring: MCP observation uses the render limit",
|
|
264
|
+
bridgeSrc.includes("observationLimit: ENTWURF_PEERS_RENDER_LIMIT"),
|
|
265
|
+
);
|
|
266
|
+
ok(
|
|
267
|
+
"#112 wiring: native pi observation uses the render limit",
|
|
268
|
+
nativeSrc.includes("observationLimit: render.ENTWURF_PEERS_RENDER_LIMIT"),
|
|
269
|
+
);
|
|
258
270
|
ok(
|
|
259
271
|
"wiring: bridge does not paste full JSON payload into human text",
|
|
260
272
|
!bridgeSrc.includes("JSON.stringify(payload)"),
|
|
@@ -266,14 +266,14 @@ ok(
|
|
|
266
266
|
/existsSync\s*\(/.test(piBody) && /controlSocketPathIn\s*\(\s*ENTWURF_DIR\s*,/.test(piBody),
|
|
267
267
|
);
|
|
268
268
|
|
|
269
|
-
const metaBuilder = functionBody("
|
|
269
|
+
const metaBuilder = functionBody("buildMetaSenderEnvelope");
|
|
270
270
|
// FIRST of the two derivation claims so a binary-fallback mutant dies here, not at the seam pin.
|
|
271
271
|
ok(
|
|
272
|
-
"
|
|
272
|
+
"buildMetaSenderEnvelope does not fall back every non-native-push backend to self-fetch [QK:SELFADDR-NO-FALLBACK-SELF-FETCH]",
|
|
273
273
|
!/\?\s*"native-push"\s*:\s*"self-fetch"/.test(metaBuilder),
|
|
274
274
|
);
|
|
275
275
|
ok(
|
|
276
|
-
"
|
|
276
|
+
"buildMetaSenderEnvelope derives self-fetch through resolveMailboxWakeModeCapability(identity), not a backend-name list [QK:SELFADDR-MAILBOX-WAKE-SEAM]",
|
|
277
277
|
/resolveMailboxWakeModeCapability\s*\(\s*identity\s*\)/.test(metaBuilder),
|
|
278
278
|
);
|
|
279
279
|
|
|
@@ -443,14 +443,13 @@ function metaRailRenderIsHonest(region: string): { honest: boolean; reason: stri
|
|
|
443
443
|
throw new Error(`class ${name} body never closed`);
|
|
444
444
|
};
|
|
445
445
|
|
|
446
|
-
// Anchored per class (not a repo-wide grep) so a mention
|
|
447
|
-
//
|
|
446
|
+
// Anchored per class (not a repo-wide grep) so a mention elsewhere cannot make
|
|
447
|
+
// either row vacuously green. Native hook mechanisms vary by backend; Codex
|
|
448
|
+
// carries request-scoped identity instead of a shared process marker.
|
|
448
449
|
for (const cls of ["EntwurfEnvelopeWiringError", "EntwurfSenderIdentityError"]) {
|
|
449
450
|
const body = classBody(cls);
|
|
450
|
-
ok(
|
|
451
|
-
|
|
452
|
-
/SessionStart/.test(body) && /PreInvocation/.test(body),
|
|
453
|
-
);
|
|
451
|
+
ok(`${cls} names a native sender marker path`, /native (sender )?(claim|hook)|sender marker/.test(body));
|
|
452
|
+
ok(`${cls} names the managed Codex request identity path`, /managed Codex/.test(body));
|
|
454
453
|
ok(
|
|
455
454
|
`${cls} no longer presents SessionStart/Claude as THE marker writer`,
|
|
456
455
|
!/The native SessionStart hook writes that marker/.test(body) &&
|
|
@@ -465,33 +464,33 @@ function metaRailRenderIsHonest(region: string): { honest: boolean; reason: stri
|
|
|
465
464
|
// THIS session's own receiver inbox can actually wake (slice-2 presence marker), not a
|
|
466
465
|
// hardcoded true. An inactive receiver must still return the meta identity (replyable:false)
|
|
467
466
|
// — degrading to null would erase who-sent and fall through to external-mcp.
|
|
468
|
-
const metaBody = functionBody("
|
|
469
|
-
ok("
|
|
470
|
-
ok("
|
|
467
|
+
const metaBody = functionBody("buildMetaSenderEnvelope");
|
|
468
|
+
ok("buildMetaSenderEnvelope calls computeSelfAddressability", /computeSelfAddressability\s*\(/.test(metaBody));
|
|
469
|
+
ok("buildMetaSenderEnvelope no longer hardcodes `replyable: true`", !/replyable:\s*true/.test(metaBody));
|
|
471
470
|
ok(
|
|
472
471
|
// #101: the identity match alone is no longer the whole answer. `entwurf_self` composes
|
|
473
472
|
// the SAME `resolveMailboxReceiverFacts` the v2 dispatch seam uses — reading the receiver
|
|
474
473
|
// marker AND, where the watch owner is the sender-marker process, the join that says that
|
|
475
474
|
// owner is still serving this garden. A citizen's self-reported replyability and what
|
|
476
475
|
// dispatch decides about it come from one measurement, so they cannot disagree.
|
|
477
|
-
"
|
|
476
|
+
"buildMetaSenderEnvelope derives active-receiver from the SHARED receiver composition (both markers)",
|
|
478
477
|
/resolveMailboxReceiverFacts\s*\(/.test(metaBody) &&
|
|
479
478
|
/readMetaReceiverMarker\s*\(/.test(metaBody) &&
|
|
480
479
|
/readMetaSenderMarker\s*\(/.test(metaBody),
|
|
481
480
|
);
|
|
482
481
|
ok(
|
|
483
|
-
"
|
|
482
|
+
"buildMetaSenderEnvelope no longer copies one match into both receiver facts",
|
|
484
483
|
!/ownerAlive:\s*active/.test(metaBody) && !/watchArmed:\s*active/.test(metaBody),
|
|
485
484
|
);
|
|
486
485
|
ok(
|
|
487
486
|
// #101 (cross-review): both markers are read with their liveness guards ON. A reader that
|
|
488
487
|
// opted out would accept a dead session's leftover file as "which garden this pid serves
|
|
489
488
|
// now", and every fixture pid in a gate is live, so nothing dynamic here could tell.
|
|
490
|
-
"
|
|
489
|
+
"buildMetaSenderEnvelope reads neither marker with the owner guard disabled",
|
|
491
490
|
!/verifyOwner:\s*false/.test(metaBody),
|
|
492
491
|
);
|
|
493
492
|
ok(
|
|
494
|
-
"
|
|
493
|
+
"buildMetaSenderEnvelope keeps meta identity + derived replyable (inactive → not null)",
|
|
495
494
|
/origin:\s*"meta-session"/.test(metaBody) && /replyable:\s*self\.replyable/.test(metaBody),
|
|
496
495
|
);
|
|
497
496
|
|
|
@@ -280,8 +280,8 @@ ok(
|
|
|
280
280
|
// ── native-push (봉인 1/2/4): a THIRD table, disjoint from pi socket + mailbox ──
|
|
281
281
|
const NATIVE_PUSH_LIVENESSES = ["alive", "dead", "indeterminate"] as const;
|
|
282
282
|
|
|
283
|
-
// domain: antigravity
|
|
284
|
-
// from
|
|
283
|
+
// domain: antigravity and Codex are native-push; pi/claude-code are NOT; the domain
|
|
284
|
+
// is DISJOINT from pi socket liveness (a backend is never in both rails).
|
|
285
285
|
eq("native-push domain: antigravity supported", nativePushSupported("antigravity"), true);
|
|
286
286
|
eq("native-push domain: pi is NOT native-push (it is pi-socket)", nativePushSupported("pi"), false);
|
|
287
287
|
eq(
|
|
@@ -289,7 +289,8 @@ eq(
|
|
|
289
289
|
nativePushSupported("claude-code"),
|
|
290
290
|
false,
|
|
291
291
|
);
|
|
292
|
-
eq("native-push domain:
|
|
292
|
+
eq("native-push domain: Codex supported", nativePushSupported("codex"), true);
|
|
293
|
+
eq("native-push domain: NATIVE_PUSH_BACKENDS exact", [...NATIVE_PUSH_BACKENDS], ["antigravity", "codex"]);
|
|
293
294
|
ok(
|
|
294
295
|
"native-push ∩ pi-socket liveness domain = ∅ (separate rails)",
|
|
295
296
|
!NATIVE_PUSH_BACKENDS.some((b) => (LIVENESS_DOMAIN_BACKENDS as readonly string[]).includes(b)),
|
|
@@ -381,7 +381,7 @@ async function main(): Promise<void> {
|
|
|
381
381
|
// ── 8: unsupported + ff + seam says undeliverable → reject, no plan, no lock ─
|
|
382
382
|
{
|
|
383
383
|
const t = mkDeps({
|
|
384
|
-
resolution: { identity: identity("
|
|
384
|
+
resolution: { identity: identity("omp"), preProbeAddressConflict: false },
|
|
385
385
|
mailboxDeliverable: false,
|
|
386
386
|
});
|
|
387
387
|
const d = await decideDispatch({ target: GID, intent: "fire-and-forget", message: "mail" }, t.deps);
|
|
@@ -399,8 +399,7 @@ async function main(): Promise<void> {
|
|
|
399
399
|
// reject, NO plan, NO lock, NO inspect/probe. This is the v2 closure of the gap slice
|
|
400
400
|
// 2d-2 closed for v1: the decider no longer trusts wake-mode alone; the required seam's
|
|
401
401
|
// active-receiver verdict governs, so a reply to a dead claude-code is refused, not
|
|
402
|
-
// enqueued as mailbox garbage.
|
|
403
|
-
// receipt level — that is the point: the seam, not the backend, decides.) ────────────
|
|
402
|
+
// enqueued as mailbox garbage. ───────────────────────────────────────────────────
|
|
404
403
|
{
|
|
405
404
|
const t = mkDeps({
|
|
406
405
|
resolution: { identity: identity("claude-code"), preProbeAddressConflict: false },
|
|
@@ -581,7 +580,7 @@ async function main(): Promise<void> {
|
|
|
581
580
|
// ff × alive → execute native-push send; plan carries route/backend/nativeSessionId; lock null.
|
|
582
581
|
const t = mkDeps({
|
|
583
582
|
resolution: npResolution,
|
|
584
|
-
nativePush: { status: "alive", route: { lsAddress: "127.0.0.1:5599" } },
|
|
583
|
+
nativePush: { status: "alive", route: { backend: "antigravity", lsAddress: "127.0.0.1:5599" } },
|
|
585
584
|
});
|
|
586
585
|
const d = await decideDispatch({ target: GID, intent: "fire-and-forget", message: "yo" }, t.deps);
|
|
587
586
|
ok("native-push ff+alive: execute", isExecute(d));
|
|
@@ -594,7 +593,10 @@ async function main(): Promise<void> {
|
|
|
594
593
|
t.mailboxCalls.length === 0,
|
|
595
594
|
);
|
|
596
595
|
if (isExecute(d) && d.plan.transport === "native-push") {
|
|
597
|
-
ok(
|
|
596
|
+
ok(
|
|
597
|
+
"native-push ff+alive: plan route = probed route",
|
|
598
|
+
d.plan.route.backend === "antigravity" && d.plan.route.lsAddress === "127.0.0.1:5599",
|
|
599
|
+
);
|
|
598
600
|
ok("native-push ff+alive: plan backend = antigravity", d.plan.backend === "antigravity");
|
|
599
601
|
ok("native-push ff+alive: plan nativeSessionId from identity", d.plan.nativeSessionId === `native-${GID}`);
|
|
600
602
|
ok(
|
|
@@ -35,7 +35,11 @@ function ok(label: string, cond: boolean): void {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const CONV = "conv-xyz";
|
|
38
|
-
const PLANTED: NativePushRoute = { lsAddress: "127.0.0.1:5600" };
|
|
38
|
+
const PLANTED: NativePushRoute = { backend: "antigravity", lsAddress: "127.0.0.1:5600" };
|
|
39
|
+
|
|
40
|
+
function agyAddress(route: NativePushRoute | undefined): string | undefined {
|
|
41
|
+
return route?.backend === "antigravity" ? route.lsAddress : undefined;
|
|
42
|
+
}
|
|
39
43
|
|
|
40
44
|
interface SendCall {
|
|
41
45
|
route: NativePushRoute;
|
|
@@ -48,6 +52,7 @@ interface FakeAdapterConfig {
|
|
|
48
52
|
probes: NativePushProbeResult[];
|
|
49
53
|
/** 0-based indices of send() calls that THROW. */
|
|
50
54
|
sendFailAt?: number[];
|
|
55
|
+
retriable?: boolean;
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
function makeFakeAdapter(config: FakeAdapterConfig): {
|
|
@@ -60,6 +65,7 @@ function makeFakeAdapter(config: FakeAdapterConfig): {
|
|
|
60
65
|
const sends: SendCall[] = [];
|
|
61
66
|
const adapter: NativePushAdapter = {
|
|
62
67
|
id: "antigravity",
|
|
68
|
+
retriable: config.retriable ?? true,
|
|
63
69
|
async probe() {
|
|
64
70
|
const p = config.probes[probeIdx] ?? { status: "dead", reason: "fake: probes exhausted" };
|
|
65
71
|
probeIdx++;
|
|
@@ -81,31 +87,53 @@ async function main(): Promise<void> {
|
|
|
81
87
|
const r = await deliverViaNativePush(adapter, PLANTED, CONV, "hi");
|
|
82
88
|
ok("success: retried:false", r.success === true && r.retried === false);
|
|
83
89
|
ok("success: exactly ONE send", sends.length === 1);
|
|
84
|
-
ok("success: send used the PLANTED route (no re-probe)", sends[0]?.route
|
|
90
|
+
ok("success: send used the PLANTED route (no re-probe)", agyAddress(sends[0]?.route) === "127.0.0.1:5600");
|
|
85
91
|
ok("success: ZERO re-probe", probeCount() === 0);
|
|
86
92
|
}
|
|
87
93
|
|
|
94
|
+
// Codex's vendor queue may accept before its receipt fails: no re-probe, no replay.
|
|
95
|
+
{
|
|
96
|
+
const { adapter, sends, probeCount } = makeFakeAdapter({
|
|
97
|
+
probes: [{ status: "alive", route: { backend: "antigravity", lsAddress: "must-not-be-used" } }],
|
|
98
|
+
sendFailAt: [0, 1],
|
|
99
|
+
retriable: false,
|
|
100
|
+
});
|
|
101
|
+
await assert.rejects(() => deliverViaNativePush(adapter, PLANTED, CONV, "hi"), /fake send fail/);
|
|
102
|
+
ok("non-retriable failure makes exactly ONE send", sends.length === 1);
|
|
103
|
+
ok("non-retriable failure makes ZERO probes", probeCount() === 0);
|
|
104
|
+
}
|
|
105
|
+
|
|
88
106
|
// ── fail → re-probe alive → re-send success: retried, fresh route ────────────
|
|
89
107
|
{
|
|
90
108
|
const { adapter, sends, probeCount } = makeFakeAdapter({
|
|
91
|
-
probes: [{ status: "alive", route: { lsAddress: "127.0.0.1:5601" } }],
|
|
109
|
+
probes: [{ status: "alive", route: { backend: "antigravity", lsAddress: "127.0.0.1:5601" } }],
|
|
92
110
|
sendFailAt: [0],
|
|
93
111
|
});
|
|
94
112
|
const r = await deliverViaNativePush(adapter, PLANTED, CONV, "hi");
|
|
95
113
|
ok("retry: retried:true", r.success === true && r.retried === true);
|
|
96
114
|
ok("retry: TWO sends (initial + one retry)", sends.length === 2);
|
|
97
115
|
ok("retry: exactly ONE re-probe", probeCount() === 1);
|
|
98
|
-
ok("retry: 1st send used the planted route", sends[0]?.route
|
|
116
|
+
ok("retry: 1st send used the planted route", agyAddress(sends[0]?.route) === "127.0.0.1:5600");
|
|
99
117
|
ok(
|
|
100
118
|
"retry: 2nd send used the RE-DISCOVERED route (not the stale one)",
|
|
101
|
-
sends[1]?.route
|
|
119
|
+
agyAddress(sends[1]?.route) === "127.0.0.1:5601",
|
|
120
|
+
);
|
|
121
|
+
// The retry replays CONTENT, never re-derives it. The caller hands this leaf one already
|
|
122
|
+
// rendered string (the #95 sender envelope is rendered ONCE, upstream, with its own
|
|
123
|
+
// timestamp), so the only honest retry is the same bytes — a second render, or any
|
|
124
|
+
// per-attempt decoration here, would deliver a body that disagrees with the first
|
|
125
|
+
// attempt the target may already have accepted. Asserted at this leaf so the claim holds
|
|
126
|
+
// without coupling sender rendering into the adapter hand.
|
|
127
|
+
ok(
|
|
128
|
+
"[QK:NATIVE-PUSH-RETRY-BYTE-IDENTITY] the retry replays byte-identical content, never a re-derived body",
|
|
129
|
+
sends[0]?.content === sends[1]?.content,
|
|
102
130
|
);
|
|
103
131
|
}
|
|
104
132
|
|
|
105
133
|
// ── fail → re-probe alive → re-send FAIL: throws (fail-loud, no 3rd attempt) ──
|
|
106
134
|
{
|
|
107
135
|
const { adapter, sends } = makeFakeAdapter({
|
|
108
|
-
probes: [{ status: "alive", route: { lsAddress: "127.0.0.1:5601" } }],
|
|
136
|
+
probes: [{ status: "alive", route: { backend: "antigravity", lsAddress: "127.0.0.1:5601" } }],
|
|
109
137
|
sendFailAt: [0, 1],
|
|
110
138
|
});
|
|
111
139
|
let threw = false;
|
|
@@ -182,7 +210,7 @@ async function main(): Promise<void> {
|
|
|
182
210
|
);
|
|
183
211
|
ok(
|
|
184
212
|
"makeNativePushSend: sent the plan message over the plan route",
|
|
185
|
-
sends[0]?.content === "payload" && sends[0]?.route
|
|
213
|
+
sends[0]?.content === "payload" && agyAddress(sends[0]?.route) === "127.0.0.1:5600",
|
|
186
214
|
);
|
|
187
215
|
ok("makeNativePushSend: lock IGNORED (lock-free — a bogus lock did not break delivery)", sends.length === 1);
|
|
188
216
|
}
|
|
@@ -27,8 +27,14 @@ import {
|
|
|
27
27
|
makeProductionEntwurfV2Deps,
|
|
28
28
|
type ProductionEntwurfV2Opts,
|
|
29
29
|
} from "../pi-extensions/lib/entwurf-v2-production.ts";
|
|
30
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
type EntwurfV2RunDeps,
|
|
32
|
+
type EntwurfV2RunResult,
|
|
33
|
+
executeDispatch,
|
|
34
|
+
runEntwurfV2,
|
|
35
|
+
} from "../pi-extensions/lib/entwurf-v2-runner.ts";
|
|
31
36
|
import type { ControlSocketPlan, MetaMailboxPlan } from "../pi-extensions/lib/entwurf-v2-send.ts";
|
|
37
|
+
import { formatMetaMailboxBody, type MailboxSenderEnvelope } from "../pi-extensions/lib/meta-mailbox-body.ts";
|
|
32
38
|
import type { MetaIdentity, MetaReceiverMarker, MetaSenderMarker } from "../pi-extensions/lib/meta-session.ts";
|
|
33
39
|
import type { NativePushAdapter, NativePushProbeResult } from "../pi-extensions/lib/native-push/adapter.ts";
|
|
34
40
|
import type { TargetSocketInspection } from "../pi-extensions/lib/socket-discovery.ts";
|
|
@@ -43,6 +49,13 @@ function ok(label: string, cond: boolean): void {
|
|
|
43
49
|
const GID = "20260613T100000-aaaaaa";
|
|
44
50
|
/** The garden a switched-away owner serves instead (#101). */
|
|
45
51
|
const OTHER_GID = "20260613T100000-bbbbbb";
|
|
52
|
+
/** The one trusted sender every rail in this gate renders from. */
|
|
53
|
+
const SENDER: MailboxSenderEnvelope = {
|
|
54
|
+
sessionId: "self",
|
|
55
|
+
agentId: "pi/x",
|
|
56
|
+
cwd: "/cwd",
|
|
57
|
+
timestamp: "2026-06-13T00:00:00.000Z",
|
|
58
|
+
};
|
|
46
59
|
const LOCK_DIR = "/fake/locks";
|
|
47
60
|
const SESSIONS_DIR = "/fake/sessions";
|
|
48
61
|
const MAILBOX_DIR = "/fake/mailbox";
|
|
@@ -132,7 +145,36 @@ interface Spies {
|
|
|
132
145
|
/** which backend the native-push adapter resolver was asked for (decide + execute). */
|
|
133
146
|
nativePushResolve: string[];
|
|
134
147
|
nativePushProbe: { conv: string }[];
|
|
135
|
-
nativePushSend: {
|
|
148
|
+
nativePushSend: { route: string; conv: string; content: string }[];
|
|
149
|
+
/** every `senderProvider()` call — ONE provider feeds all three rails, so a hand that
|
|
150
|
+
* resolves the envelope twice (or not at all) is visible here. */
|
|
151
|
+
senderCalls: number[];
|
|
152
|
+
/** ORDERED trace of the native-push hand's observable steps. Counts alone cannot see
|
|
153
|
+
* ORDER, and order is the whole safety claim: the envelope must be resolved BEFORE the
|
|
154
|
+
* adapter is touched, so a provider throw leaves the target's conversation untouched
|
|
155
|
+
* rather than half-delivered. `runInTwoPhases` marks where the execute phase starts. */
|
|
156
|
+
events: ("sender" | "resolve" | "probe" | "send")[];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* `runEntwurfV2` split at its own seam so the EXECUTE phase has a MEASURED boundary. The decide
|
|
161
|
+
* phase legitimately resolves the adapter and probes (봉인 4: one adapter for both hands), so an
|
|
162
|
+
* order claim about the send hand has to start after decide returns — but the boundary must be
|
|
163
|
+
* the index recorded between the two calls, never something inferred from the event VALUES. An
|
|
164
|
+
* earlier version sliced after the last `probe`, which a regression could defeat: an executor
|
|
165
|
+
* that resolved and probed before the sender would make its own probe the "last" one and hide
|
|
166
|
+
* the illegal prefix it just produced. This records the length instead, so ANY extra execute-side
|
|
167
|
+
* resolve or probe ahead of the sender stays inside the slice and fails.
|
|
168
|
+
*/
|
|
169
|
+
async function runInTwoPhases(
|
|
170
|
+
input: DispatchInput,
|
|
171
|
+
deps: EntwurfV2RunDeps,
|
|
172
|
+
spies: Spies,
|
|
173
|
+
): Promise<{ result: EntwurfV2RunResult; executeEvents: string[] }> {
|
|
174
|
+
const decision = await deps.decide(input);
|
|
175
|
+
const executeStart = spies.events.length;
|
|
176
|
+
const result = await executeDispatch(decision, deps.executor);
|
|
177
|
+
return { result, executeEvents: spies.events.slice(executeStart) };
|
|
136
178
|
}
|
|
137
179
|
|
|
138
180
|
/** Build a factory whose every leaf IO is a spy. `over` lets a case shape the decision
|
|
@@ -146,6 +188,8 @@ function makeSpiedFactory(over: {
|
|
|
146
188
|
classifyDead?: boolean;
|
|
147
189
|
/** #50 C3 — a caller with no authoritative sender (senderProvider → undefined). */
|
|
148
190
|
noSender?: boolean;
|
|
191
|
+
/** #95 — the envelope source itself fails: the send must reject with NOTHING injected. */
|
|
192
|
+
senderThrows?: boolean;
|
|
149
193
|
/** the native-push adapter probe result (only reached on a native-push backend). */
|
|
150
194
|
nativePushProbe?: NativePushProbeResult;
|
|
151
195
|
/** SE-2 2d-3 — the target's receiver presence marker: "active" (matches identity,
|
|
@@ -166,12 +210,16 @@ function makeSpiedFactory(over: {
|
|
|
166
210
|
nativePushResolve: [],
|
|
167
211
|
nativePushProbe: [],
|
|
168
212
|
nativePushSend: [],
|
|
213
|
+
senderCalls: [],
|
|
214
|
+
events: [],
|
|
169
215
|
};
|
|
170
216
|
const opts: ProductionEntwurfV2Opts = {
|
|
171
|
-
senderProvider: () =>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
217
|
+
senderProvider: () => {
|
|
218
|
+
spies.senderCalls.push(spies.senderCalls.length + 1);
|
|
219
|
+
spies.events.push("sender");
|
|
220
|
+
if (over.senderThrows) throw new Error("fake: sender envelope unavailable");
|
|
221
|
+
return over.noSender ? undefined : SENDER;
|
|
222
|
+
},
|
|
175
223
|
lockDir: LOCK_DIR,
|
|
176
224
|
sessionsDir: SESSIONS_DIR,
|
|
177
225
|
mailboxDir: MAILBOX_DIR,
|
|
@@ -231,14 +279,19 @@ function makeSpiedFactory(over: {
|
|
|
231
279
|
// two hands never resolve different adapters.
|
|
232
280
|
resolveNativePushAdapter: (backend: string): NativePushAdapter => {
|
|
233
281
|
spies.nativePushResolve.push(backend);
|
|
282
|
+
spies.events.push("resolve");
|
|
234
283
|
return {
|
|
235
|
-
id: "
|
|
284
|
+
id: backend as NativePushAdapter["id"],
|
|
285
|
+
retriable: backend === "antigravity",
|
|
236
286
|
async probe(conv) {
|
|
237
287
|
spies.nativePushProbe.push({ conv });
|
|
288
|
+
spies.events.push("probe");
|
|
238
289
|
return over.nativePushProbe ?? { status: "dead", reason: "fake: no native-push probe configured" };
|
|
239
290
|
},
|
|
240
291
|
async send(route, conv, content) {
|
|
241
|
-
|
|
292
|
+
const routeLabel = route.backend === "antigravity" ? route.lsAddress : route.socketPath;
|
|
293
|
+
spies.nativePushSend.push({ route: routeLabel, conv, content });
|
|
294
|
+
spies.events.push("send");
|
|
242
295
|
},
|
|
243
296
|
};
|
|
244
297
|
},
|
|
@@ -277,14 +330,14 @@ async function main(): Promise<void> {
|
|
|
277
330
|
{
|
|
278
331
|
const { deps, spies } = makeSpiedFactory({
|
|
279
332
|
backend: "antigravity",
|
|
280
|
-
nativePushProbe: { status: "alive", route: { lsAddress: "127.0.0.1:5599" } },
|
|
333
|
+
nativePushProbe: { status: "alive", route: { backend: "antigravity", lsAddress: "127.0.0.1:5599" } },
|
|
281
334
|
});
|
|
282
335
|
const result = await runEntwurfV2({ target: GID, intent: "fire-and-forget", message: "hi agy" }, deps);
|
|
283
336
|
ok("A3: antigravity ff → native-push delivered", result.kind === "executed" && result.transport === "native-push");
|
|
284
337
|
ok("A3: decider probed the native-push adapter once (decide side)", spies.nativePushProbe.length === 1);
|
|
285
338
|
ok("A3: executor sent via the native-push adapter once (execute side)", spies.nativePushSend.length === 1);
|
|
286
|
-
ok("A3: send used the DECIDER-probed route", spies.nativePushSend[0]?.
|
|
287
|
-
ok("A3: send carried the dispatch message", spies.nativePushSend[0]?.content
|
|
339
|
+
ok("A3: send used the DECIDER-probed route", spies.nativePushSend[0]?.route === "127.0.0.1:5599");
|
|
340
|
+
ok("A3: send carried the dispatch message", spies.nativePushSend[0]?.content.includes("hi agy") === true);
|
|
288
341
|
ok(
|
|
289
342
|
"A3: BOTH hands resolved the SAME adapter (for 'antigravity')",
|
|
290
343
|
spies.nativePushResolve.length === 2 && spies.nativePushResolve.every((b) => b === "antigravity"),
|
|
@@ -292,6 +345,145 @@ async function main(): Promise<void> {
|
|
|
292
345
|
ok("A3: native-push is LOCK-FREE (no acquire)", spies.acquire.length === 0);
|
|
293
346
|
ok("A3: native-push did NOT enqueue a mailbox (not the unsupported path)", spies.enqueue.length === 0);
|
|
294
347
|
}
|
|
348
|
+
{
|
|
349
|
+
const { deps, spies } = makeSpiedFactory({
|
|
350
|
+
backend: "codex",
|
|
351
|
+
nativePushProbe: {
|
|
352
|
+
status: "alive",
|
|
353
|
+
route: { backend: "codex", socketPath: "/home/operator/.codex/app-server-control/app-server-control.sock" },
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
const result = await runEntwurfV2({ target: GID, intent: "fire-and-forget", message: "hi codex" }, deps);
|
|
357
|
+
ok(
|
|
358
|
+
"[QK:CODEX-NATIVE-PRODUCTION-WIRING] Codex ff → native-push delivered",
|
|
359
|
+
result.kind === "executed" && result.transport === "native-push",
|
|
360
|
+
);
|
|
361
|
+
ok(
|
|
362
|
+
"A3c: both hands resolve the Codex adapter",
|
|
363
|
+
spies.nativePushResolve.length === 2 && spies.nativePushResolve.every((b) => b === "codex"),
|
|
364
|
+
);
|
|
365
|
+
ok(
|
|
366
|
+
"A3c: planted Codex UDS reaches the send hand",
|
|
367
|
+
spies.nativePushSend[0]?.route.endsWith("/app-server-control.sock") === true,
|
|
368
|
+
);
|
|
369
|
+
ok(
|
|
370
|
+
"A3c: Codex native-push remains lock-free and mailbox-free",
|
|
371
|
+
spies.acquire.length === 0 && spies.enqueue.length === 0,
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
// ── A3d (#95): the native-push rail injects the MAILBOX-SERIALIZED sender envelope ──
|
|
375
|
+
// Measured in the #95 A LIVE run: a nonce-only fresh-call callback direct-injected into a
|
|
376
|
+
// Codex thread arrived as bare text, so the receiving citizen could not name who called it
|
|
377
|
+
// and the callback was uncorrelatable. Direct injection has neither the control socket's RPC
|
|
378
|
+
// framing nor a mailbox file, so the envelope must ride IN the body — the same bytes
|
|
379
|
+
// `formatMetaMailboxBody` gives the mailbox rail. ONE rail: both native-push backends, no
|
|
380
|
+
// per-backend branch.
|
|
381
|
+
{
|
|
382
|
+
const route = { backend: "antigravity", lsAddress: "127.0.0.1:5599" } as const;
|
|
383
|
+
const { deps, spies } = makeSpiedFactory({
|
|
384
|
+
backend: "antigravity",
|
|
385
|
+
nativePushProbe: { status: "alive", route },
|
|
386
|
+
});
|
|
387
|
+
const { result, executeEvents } = await runInTwoPhases(
|
|
388
|
+
{ target: GID, intent: "fire-and-forget", message: "hi agy", wantsReply: true },
|
|
389
|
+
deps,
|
|
390
|
+
spies,
|
|
391
|
+
);
|
|
392
|
+
// WIRING FIRST, and it is its own claim: the factory must hand the native-push hand the
|
|
393
|
+
// SAME `opts.senderProvider` the mailbox hand gets. A render that is reachable but never
|
|
394
|
+
// fed falls back to raw silently, so "the provider was consulted exactly once on this
|
|
395
|
+
// rail" is asserted BEFORE the body is compared — that way a dropped wiring fails here
|
|
396
|
+
// at its own QK instead of borrowing the envelope claim's failure.
|
|
397
|
+
ok(
|
|
398
|
+
"[QK:NATIVE-PUSH-SENDER-PROVIDER-WIRING] the factory fed the native-push hand its one senderProvider, exactly once",
|
|
399
|
+
result.kind === "executed" && spies.senderCalls.length === 1,
|
|
400
|
+
);
|
|
401
|
+
ok(
|
|
402
|
+
"[QK:NATIVE-PUSH-SENDER-ENVELOPE] agy direct injection carries the mailbox-serialized trusted envelope",
|
|
403
|
+
spies.nativePushSend[0]?.content === formatMetaMailboxBody(SENDER, "hi agy", true),
|
|
404
|
+
);
|
|
405
|
+
ok(
|
|
406
|
+
"A3d: the enveloped body is NOT the raw plan message (an envelope was actually added)",
|
|
407
|
+
spies.nativePushSend[0]?.content !== "hi agy",
|
|
408
|
+
);
|
|
409
|
+
ok(
|
|
410
|
+
"A3d: wants_reply rides the envelope on this rail",
|
|
411
|
+
spies.nativePushSend[0]?.content.includes("wants reply: yes") === true,
|
|
412
|
+
);
|
|
413
|
+
// ORDER, not counts. The envelope must be resolved BEFORE the adapter is resolved or
|
|
414
|
+
// touched. Counts would be identical if the hand resolved the adapter first and the
|
|
415
|
+
// provider second — but then a provider throw would land after the adapter was already
|
|
416
|
+
// in hand, and the safety claim ("a failing envelope source injects nothing") would
|
|
417
|
+
// rest on luck rather than sequence.
|
|
418
|
+
ok(
|
|
419
|
+
"[QK:NATIVE-PUSH-SENDER-BEFORE-ADAPTER] the send hand resolves the envelope, THEN the adapter, THEN sends",
|
|
420
|
+
JSON.stringify(executeEvents) === JSON.stringify(["sender", "resolve", "send"]),
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
{
|
|
424
|
+
// Same rail, other backend — codex renders identically. A backend branch would show here.
|
|
425
|
+
const route = {
|
|
426
|
+
backend: "codex",
|
|
427
|
+
socketPath: "/home/operator/.codex/app-server-control/app-server-control.sock",
|
|
428
|
+
} as const;
|
|
429
|
+
const { deps, spies } = makeSpiedFactory({ backend: "codex", nativePushProbe: { status: "alive", route } });
|
|
430
|
+
const result = await runEntwurfV2({ target: GID, intent: "fire-and-forget", message: "hi codex" }, deps);
|
|
431
|
+
ok(
|
|
432
|
+
"A3d: codex direct injection carries the SAME mailbox-serialized envelope (one rail, no backend branch)",
|
|
433
|
+
result.kind === "executed" &&
|
|
434
|
+
spies.nativePushSend[0]?.content === formatMetaMailboxBody(SENDER, "hi codex", false),
|
|
435
|
+
);
|
|
436
|
+
ok("A3d: the codex body is NOT the raw plan message either", spies.nativePushSend[0]?.content !== "hi codex");
|
|
437
|
+
ok("A3d: codex resolved the envelope EXACTLY once", spies.senderCalls.length === 1);
|
|
438
|
+
}
|
|
439
|
+
{
|
|
440
|
+
// #50 C3 parity: no authoritative sender → the raw message is preserved, never a
|
|
441
|
+
// fabricated envelope. This is the same fallback the mailbox hand has.
|
|
442
|
+
const { deps, spies } = makeSpiedFactory({
|
|
443
|
+
backend: "codex",
|
|
444
|
+
noSender: true,
|
|
445
|
+
nativePushProbe: {
|
|
446
|
+
status: "alive",
|
|
447
|
+
route: { backend: "codex", socketPath: "/home/operator/.codex/app-server-control/app-server-control.sock" },
|
|
448
|
+
},
|
|
449
|
+
});
|
|
450
|
+
const result = await runEntwurfV2({ target: GID, intent: "fire-and-forget", message: "raw only" }, deps);
|
|
451
|
+
ok(
|
|
452
|
+
"A3d: no authoritative sender → the RAW message is injected, no fabricated envelope",
|
|
453
|
+
result.kind === "executed" && spies.nativePushSend[0]?.content === "raw only",
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
{
|
|
457
|
+
// A throwing provider must reject the send with NOTHING injected — a half-delivered
|
|
458
|
+
// conversation is worse than a rejected dispatch. No new reject reason, no fallback.
|
|
459
|
+
const { deps, spies } = makeSpiedFactory({
|
|
460
|
+
backend: "codex",
|
|
461
|
+
senderThrows: true,
|
|
462
|
+
nativePushProbe: {
|
|
463
|
+
status: "alive",
|
|
464
|
+
route: { backend: "codex", socketPath: "/home/operator/.codex/app-server-control/app-server-control.sock" },
|
|
465
|
+
},
|
|
466
|
+
});
|
|
467
|
+
const { result, executeEvents } = await runInTwoPhases(
|
|
468
|
+
{ target: GID, intent: "fire-and-forget", message: "never sent" },
|
|
469
|
+
deps,
|
|
470
|
+
spies,
|
|
471
|
+
);
|
|
472
|
+
ok(
|
|
473
|
+
"A3d: a throwing sender provider fails the dispatch EXACTLY as execution-failed",
|
|
474
|
+
result.kind === "execution-failed",
|
|
475
|
+
);
|
|
476
|
+
ok("A3d: a throwing sender provider called its provider exactly once", spies.senderCalls.length === 1);
|
|
477
|
+
ok("A3d: a throwing sender provider never resolved the adapter", executeEvents.includes("resolve") === false);
|
|
478
|
+
ok("A3d: a throwing sender provider injected NOTHING", spies.nativePushSend.length === 0);
|
|
479
|
+
// The whole execute phase is ONE event: the envelope source was asked, it failed, and the
|
|
480
|
+
// target's conversation was never reached. No partial delivery, no adapter in hand.
|
|
481
|
+
ok(
|
|
482
|
+
"A3d: the failed execute phase is exactly ['sender'] — nothing downstream ran",
|
|
483
|
+
JSON.stringify(executeEvents) === JSON.stringify(["sender"]),
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
295
487
|
{
|
|
296
488
|
// antigravity + dead probe → reject native-push-target-dead, NO send.
|
|
297
489
|
const { deps, spies } = makeSpiedFactory({
|
|
@@ -101,7 +101,7 @@ const NATIVE_PUSH_PLAN: NativePushPlan = {
|
|
|
101
101
|
targetGardenId: GID,
|
|
102
102
|
backend: "antigravity",
|
|
103
103
|
nativeSessionId: "conv-xyz",
|
|
104
|
-
route: { lsAddress: "127.0.0.1:5599" },
|
|
104
|
+
route: { backend: "antigravity", lsAddress: "127.0.0.1:5599" },
|
|
105
105
|
wantsReply: false,
|
|
106
106
|
message: "m",
|
|
107
107
|
};
|
|
@@ -729,7 +729,7 @@ async function main(): Promise<void> {
|
|
|
729
729
|
v2Start !== -1 && after > v2Start,
|
|
730
730
|
);
|
|
731
731
|
const v2Block = src.slice(v2Start, after);
|
|
732
|
-
ok("5: MCP — v2 handler builds buildSendSenderEnvelope()", /buildSendSenderEnvelope\(
|
|
732
|
+
ok("5: MCP — v2 handler builds buildSendSenderEnvelope(context)", /buildSendSenderEnvelope\s*\(/.test(v2Block));
|
|
733
733
|
ok("5: MCP — v2 handler passes senderProvider: () => sender", /senderProvider:\s*\(\)\s*=>\s*sender/.test(v2Block));
|
|
734
734
|
ok(
|
|
735
735
|
"5: MCP — v2 handler calls runAndRenderEntwurfV2FromSurface",
|
|
@@ -832,9 +832,12 @@ let manifestCount: number;
|
|
|
832
832
|
"bridge-boot-resume": 3,
|
|
833
833
|
"bridge-command-boot": 9,
|
|
834
834
|
"capability-cache": 3,
|
|
835
|
+
"codex-native": 68,
|
|
836
|
+
"compaction-send-guard": 7,
|
|
835
837
|
"copilot-birth": 19,
|
|
836
838
|
"copilot-launch": 14,
|
|
837
839
|
"copilot-receive": 20,
|
|
840
|
+
"entwurf-peers": 1,
|
|
838
841
|
"fresh-cut": 3,
|
|
839
842
|
"gate-qualification": 2,
|
|
840
843
|
"meta-facts": 4,
|
|
@@ -842,23 +845,23 @@ let manifestCount: number;
|
|
|
842
845
|
"meta-identity": 4,
|
|
843
846
|
"meta-retire": 3,
|
|
844
847
|
"mux-boundary": 16,
|
|
845
|
-
"mux-fresh-call":
|
|
848
|
+
"mux-fresh-call": 45,
|
|
846
849
|
"mux-launcher-fence": 7,
|
|
847
850
|
"mux-parent-artifact": 3,
|
|
848
851
|
"pack-install": 2,
|
|
849
|
-
"pi-package-ownership":
|
|
852
|
+
"pi-package-ownership": 8,
|
|
850
853
|
"mux-resume-call": 12,
|
|
851
854
|
"omp-birth": 13,
|
|
852
855
|
"omp-fresh": 24,
|
|
853
856
|
"omp-receive": 11,
|
|
854
857
|
"probe-ordering": 1,
|
|
855
|
-
"release-gate":
|
|
858
|
+
"release-gate": 19,
|
|
856
859
|
"resume-args": 6,
|
|
857
860
|
"resume-launch-identity": 6,
|
|
858
861
|
"self-address": 5,
|
|
859
862
|
"setup-verdict": 14,
|
|
860
863
|
"source-install": 2,
|
|
861
|
-
"v2-surface":
|
|
864
|
+
"v2-surface": 11,
|
|
862
865
|
"v2-visible-resume": 17,
|
|
863
866
|
};
|
|
864
867
|
const laneTally: Record<string, number> = {};
|
|
@@ -81,7 +81,6 @@ const PRE_82_LEGACY: Partial<Record<MetaCitizenBackend, [file: string, sentence:
|
|
|
81
81
|
"DELIVERY.md",
|
|
82
82
|
"Admitted before the #82 step 9 contract and not re-evaluated under it, so it is legacy citizen evidence, not a step-9 supported harness",
|
|
83
83
|
],
|
|
84
|
-
codex: ["DELIVERY.md", "No owned native-citizen install/invocation lane."],
|
|
85
84
|
};
|
|
86
85
|
|
|
87
86
|
const fresh = new Set<string>(FRESH_CALL_BACKENDS);
|