@junghanacs/entwurf 0.22.0 → 0.23.1
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 -1
- package/BASELINE.md +1 -0
- package/CHANGELOG.md +514 -0
- package/DELIVERY.md +10 -2
- package/README.md +43 -4
- package/VERIFY.md +47 -0
- package/docs/acp-backend-rail.md +96 -17
- package/docs/setup-clean-host.md +11 -1
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +39 -40
- 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 +2 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-declaration.js +609 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +95 -56
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +40 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +6 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-facts.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peer-observe.js +67 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +9 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +24 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +423 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +98 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +1018 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-placement.js +213 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +46 -273
- package/mcp/entwurf-bridge/dist/scripts/peer-facts.js +109 -0
- package/mcp/entwurf-bridge/src/index.ts +41 -41
- package/mcp/entwurf-bridge/tsconfig.build.json +1 -0
- package/package.json +9 -8
- package/pi-extensions/entwurf-control.ts +91 -52
- 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 +4 -4
- package/pi-extensions/lib/acp/event-mapper.ts +5 -5
- package/pi-extensions/lib/acp/tool-surface.ts +2 -1
- package/pi-extensions/lib/codex-declaration.js +612 -0
- package/pi-extensions/lib/codex-fresh-preflight.ts +107 -51
- package/pi-extensions/lib/entwurf-control-rpc.ts +46 -0
- package/pi-extensions/lib/entwurf-fact-provider.ts +13 -2
- package/pi-extensions/lib/entwurf-facts.ts +14 -1
- package/pi-extensions/lib/entwurf-peer-observe.ts +72 -2
- package/pi-extensions/lib/entwurf-peers-render.ts +9 -1
- package/pi-extensions/lib/entwurf-self-address.ts +33 -0
- package/pi-extensions/lib/entwurf-v2-contract.ts +2 -2
- package/pi-extensions/lib/entwurf-v2-lock.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-runner.ts +1 -1
- package/pi-extensions/lib/fresh-call-composition.ts +493 -0
- package/pi-extensions/lib/fresh-call-dispatch.ts +142 -0
- package/pi-extensions/lib/herdr-fresh-call.ts +1378 -0
- package/pi-extensions/lib/herdr-placement.ts +276 -0
- package/pi-extensions/lib/mux-fresh-call.ts +95 -301
- package/run.sh +588 -204
- package/scripts/check-acp-usage-accounting.ts +9 -9
- package/scripts/check-codex-birth-hook.ts +187 -0
- package/scripts/check-entwurf-control-rpc.ts +138 -0
- package/scripts/check-entwurf-fact-provider.ts +4 -2
- package/scripts/check-entwurf-peers-surface.ts +3 -1
- package/scripts/check-entwurf-self-address.ts +68 -0
- package/scripts/check-fresh-call-dispatch.ts +301 -0
- package/scripts/check-gate-qualification.ts +17 -4
- package/scripts/check-herdr-activation.ts +1293 -0
- package/scripts/check-herdr-fresh-call.ts +1250 -0
- package/scripts/check-herdr-placement.ts +383 -0
- package/scripts/check-herdr-plugin-build.ts +842 -0
- package/scripts/check-herdr-plugin-profile.ts +371 -0
- package/scripts/check-herdr-plugin.ts +523 -0
- package/scripts/check-herdr-runtime-bootstrap.ts +1264 -0
- package/scripts/check-herdr-sandbox.ts +456 -0
- package/scripts/check-herdr-supply.ts +184 -0
- package/scripts/check-mux-launcher-fence.ts +138 -0
- package/scripts/check-peer-facts.ts +331 -0
- package/scripts/check-pi-launch.ts +358 -0
- package/scripts/check-release-gate-outcomes.ts +19 -0
- package/scripts/check-typing-call-fence.ts +296 -0
- package/scripts/codex-birth-doctor.sh +159 -16
- package/scripts/codex-birth-install.sh +178 -72
- package/scripts/codex-birth-uninstall.sh +162 -7
- package/scripts/fixtures/herdr-supply.json +29 -0
- package/scripts/herdr-activation.mjs +536 -0
- package/scripts/herdr-plugin-activate.mjs +270 -0
- package/scripts/herdr-plugin-deactivate.mjs +193 -0
- package/scripts/herdr-runtime.mjs +1203 -0
- package/scripts/install-herdr-ci.sh +96 -0
- package/scripts/inventory-verification-surface.ts +86 -10
- package/scripts/lib/codex-fresh-live-protocol.ts +2 -2
- package/scripts/lib/tree-digest.ts +97 -0
- package/scripts/meta-bridge-install.sh +19 -2
- package/scripts/meta-bridge-state.py +167 -11
- package/scripts/mutants/codex-caller-seat.json +6 -8
- package/scripts/mutants/codex-native.json +195 -29
- package/scripts/mutants/control-socket-disconnect.json +57 -0
- package/scripts/mutants/fresh-call-dispatch.json +154 -0
- package/scripts/mutants/herdr-activation.json +305 -0
- package/scripts/mutants/herdr-fresh-call.json +448 -0
- package/scripts/mutants/herdr-placement.json +161 -0
- package/scripts/mutants/herdr-plugin-build.json +160 -0
- package/scripts/mutants/herdr-plugin-profile.json +202 -0
- package/scripts/mutants/herdr-plugin.json +133 -0
- package/scripts/mutants/herdr-runtime-bootstrap.json +411 -0
- package/scripts/mutants/herdr-supply.json +133 -0
- package/scripts/mutants/meta-hook-session-switch.json +2 -2
- package/scripts/mutants/mux-fresh-call.json +104 -17
- package/scripts/mutants/mux-launcher-fence.json +13 -0
- package/scripts/mutants/omp-fresh.json +4 -4
- package/scripts/mutants/peer-facts.json +98 -0
- package/scripts/mutants/pi-launch.json +113 -0
- package/scripts/mutants/resume-args.json +6 -6
- package/scripts/mutants/self-address.json +33 -0
- package/scripts/mutants/typing-call-fence.json +17 -0
- package/scripts/mutants/v2-surface.json +2 -2
- package/scripts/mutants/v2-visible-resume.json +17 -17
- package/scripts/peer-facts.ts +120 -0
- package/scripts/register-pi-package.py +8 -0
- package/scripts/register-pi-provider.py +88 -12
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-codex-birth.sh +281 -12
- package/scripts/smoke-entwurf-v2-matrix-live.ts +1 -1
- package/scripts/smoke-herdr-fresh-call-live.ts +1027 -0
- package/scripts/smoke-herdr-plugin-build-live.ts +543 -0
- package/scripts/smoke-herdr-raw-install-live.sh +849 -0
- package/scripts/tsconfig.json +5 -0
- package/scripts/check-entwurf-facts.ts +0 -385
- package/scripts/check-entwurf-resume-args.ts +0 -122
- package/scripts/check-entwurf-v2-decider.ts +0 -657
- package/scripts/check-entwurf-v2-lock.ts +0 -371
- package/scripts/check-entwurf-v2-mailbox.ts +0 -261
- package/scripts/check-entwurf-v2-matrix.ts +0 -401
- package/scripts/check-entwurf-v2-native-push.ts +0 -221
- package/scripts/check-entwurf-v2-release.ts +0 -151
- package/scripts/check-entwurf-v2-runner.ts +0 -448
- package/scripts/check-entwurf-v2-send-fallback.ts +0 -350
- package/scripts/check-entwurf-v2-send.ts +0 -458
- package/scripts/check-entwurf-v2-visible-resume.ts +0 -445
- package/scripts/check-socket-discovery.ts +0 -401
|
@@ -1,401 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* check-entwurf-v2-matrix — the 5d-5 (a) REACHABILITY + LOCK SSOT TABLE.
|
|
3
|
-
*
|
|
4
|
-
* GPT Q5 design: a deterministic gate (no API, no real pi) that fixes the 5d-5
|
|
5
|
-
* operating claim — "target kind × resulting transport × lock acquire→release×1" —
|
|
6
|
-
* as ONE readable table, so a release note can say "the matrix is closed" without a
|
|
7
|
-
* reader grepping the 12 scattered check-entwurf-v2-* gates.
|
|
8
|
-
*
|
|
9
|
-
* This is a THIN coverage gate, NOT a re-implementation of the decider. It drives
|
|
10
|
-
* the REAL `decideDispatch` over minimal injected fakes (the same shape
|
|
11
|
-
* check-entwurf-v2-decider uses) and asserts, per row, the (transport, lock class,
|
|
12
|
-
* reject reason) the decider actually produces. A final COVERAGE pass then FAILS if
|
|
13
|
-
* any transport, lock class, or pre-probe reject is missing from the table — so a
|
|
14
|
-
* future decider change that silently drops a reachability cell cannot pass.
|
|
15
|
-
*
|
|
16
|
-
* Sender-surface parity (MCP entwurf_v2 vs pi-native) is NOT re-run here: both ride
|
|
17
|
-
* the same `runAndRenderEntwurfV2FromSurface` (proven once in check-entwurf-v2-surface),
|
|
18
|
-
* and the matrix is keyed on the decider both surfaces share. Adding a second surface
|
|
19
|
-
* sweep would duplicate that proof — Q-a: "전수 실행을 두 surface로 중복하지 말 것".
|
|
20
|
-
*
|
|
21
|
-
* Lock classes (the "acquire→release×1" axis made machine-checkable):
|
|
22
|
-
* none — no lock acquired at all (pre-probe reject, or the lock-free
|
|
23
|
-
* unsupported/meta-mailbox path): acquire 0, release 0.
|
|
24
|
-
* held — in-domain execute keeps the claim for 5c re-resolve: acquire 1,
|
|
25
|
-
* release 0, decision.lock non-null.
|
|
26
|
-
* mailbox-null — unsupported execute → meta-mailbox, deliberately lock-free (?7):
|
|
27
|
-
* acquire 0, release 0, decision.lock === null.
|
|
28
|
-
* released — in-domain reject after the probe: the held lock is released
|
|
29
|
-
* exactly once: acquire 1, release 1.
|
|
30
|
-
* acquire-fail — target-locked: the acquire itself failed, nothing to release:
|
|
31
|
-
* acquire 1, release 0.
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
import assert from "node:assert/strict";
|
|
35
|
-
import {
|
|
36
|
-
type EntwurfIntent,
|
|
37
|
-
type EntwurfV2Transport,
|
|
38
|
-
isLivenessSupported,
|
|
39
|
-
} from "../pi-extensions/lib/entwurf-v2-contract.ts";
|
|
40
|
-
import {
|
|
41
|
-
type DispatchDeciderDeps,
|
|
42
|
-
type DispatchDecision,
|
|
43
|
-
decideDispatch,
|
|
44
|
-
type ExecutionPlan,
|
|
45
|
-
type RejectDiagnostic,
|
|
46
|
-
type TargetResolution,
|
|
47
|
-
} from "../pi-extensions/lib/entwurf-v2-decider.ts";
|
|
48
|
-
import type { AcquireLockResult, LockClaim } from "../pi-extensions/lib/entwurf-v2-lock.ts";
|
|
49
|
-
import type { MetaCitizenBackend, MetaIdentity } from "../pi-extensions/lib/meta-session.ts";
|
|
50
|
-
import { controlSocketPath, type TargetSocketInspection } from "../pi-extensions/lib/socket-discovery.ts";
|
|
51
|
-
import type { SocketLiveness } from "../pi-extensions/lib/socket-probe.ts";
|
|
52
|
-
|
|
53
|
-
let passed = 0;
|
|
54
|
-
function ok(label: string, cond: boolean): void {
|
|
55
|
-
assert.ok(cond, label);
|
|
56
|
-
console.log(` ok ${label}`);
|
|
57
|
-
passed++;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const GID = "20260612T100000-aaaaaa";
|
|
61
|
-
const CWD = "/home/junghan/repos/gh/entwurf";
|
|
62
|
-
|
|
63
|
-
function identity(backend: MetaCitizenBackend): MetaIdentity {
|
|
64
|
-
return {
|
|
65
|
-
schemaVersion: 3,
|
|
66
|
-
gardenId: GID,
|
|
67
|
-
backend,
|
|
68
|
-
nativeSessionId: `native-${GID}`,
|
|
69
|
-
cwd: CWD,
|
|
70
|
-
model: null,
|
|
71
|
-
transcriptPath: null,
|
|
72
|
-
createdAt: "2026-06-12T01:00:00.000Z",
|
|
73
|
-
recordUpdatedAt: "2026-06-12T01:00:00.000Z",
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function lockClaim(gardenId: string): LockClaim {
|
|
78
|
-
return {
|
|
79
|
-
gardenId,
|
|
80
|
-
pid: 4242,
|
|
81
|
-
hostname: "test-host",
|
|
82
|
-
createdAt: "2026-06-12T01:00:00.000Z",
|
|
83
|
-
nonce: "deadbeefcafef00d",
|
|
84
|
-
owner: "entwurf_v2",
|
|
85
|
-
lockPath: `/fake/locks/${gardenId}.lock`,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ── injected fakes with call tracking (decider-gate shape, minimal copy) ─────
|
|
90
|
-
interface ScenarioOpts {
|
|
91
|
-
resolution: TargetResolution;
|
|
92
|
-
lock?: "ok" | "conflict";
|
|
93
|
-
inspection?: TargetSocketInspection;
|
|
94
|
-
probe?: SocketLiveness;
|
|
95
|
-
mailboxDeliverable?: boolean;
|
|
96
|
-
}
|
|
97
|
-
interface Tracked {
|
|
98
|
-
deps: DispatchDeciderDeps;
|
|
99
|
-
acquireCalls: string[];
|
|
100
|
-
releaseCalls: LockClaim[];
|
|
101
|
-
inspectCalls: string[];
|
|
102
|
-
probeCalls: string[];
|
|
103
|
-
mailboxCalls: MetaIdentity[];
|
|
104
|
-
}
|
|
105
|
-
function mkDeps(opts: ScenarioOpts): Tracked {
|
|
106
|
-
const acquireCalls: string[] = [];
|
|
107
|
-
const releaseCalls: LockClaim[] = [];
|
|
108
|
-
const inspectCalls: string[] = [];
|
|
109
|
-
const probeCalls: string[] = [];
|
|
110
|
-
const mailboxCalls: MetaIdentity[] = [];
|
|
111
|
-
const deps: DispatchDeciderDeps = {
|
|
112
|
-
resolveTarget: () => opts.resolution,
|
|
113
|
-
acquireLock: (gardenId: string): AcquireLockResult => {
|
|
114
|
-
acquireCalls.push(gardenId);
|
|
115
|
-
if (opts.lock === "conflict") {
|
|
116
|
-
return {
|
|
117
|
-
ok: false,
|
|
118
|
-
conflict: {
|
|
119
|
-
reason: "target-locked",
|
|
120
|
-
lockPath: `/fake/locks/${gardenId}.lock`,
|
|
121
|
-
holder: lockClaim(gardenId),
|
|
122
|
-
detail: "held by a live dispatcher",
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
return { ok: true, claim: lockClaim(gardenId) };
|
|
127
|
-
},
|
|
128
|
-
releaseLock: (claim: LockClaim) => {
|
|
129
|
-
releaseCalls.push(claim);
|
|
130
|
-
},
|
|
131
|
-
inspectSocket: async (gardenId: string): Promise<TargetSocketInspection> => {
|
|
132
|
-
inspectCalls.push(gardenId);
|
|
133
|
-
return opts.inspection ?? { kind: "absent", socketPath: controlSocketPath(gardenId, "/fake/ctl") };
|
|
134
|
-
},
|
|
135
|
-
probeSocket: async (socketPath: string): Promise<SocketLiveness> => {
|
|
136
|
-
probeCalls.push(socketPath);
|
|
137
|
-
return opts.probe ?? "dead";
|
|
138
|
-
},
|
|
139
|
-
mailboxDeliverabilityFor: (id: MetaIdentity) => {
|
|
140
|
-
mailboxCalls.push(id);
|
|
141
|
-
return { deliverable: opts.mailboxDeliverable ?? false, reason: "fake-deliverability" };
|
|
142
|
-
},
|
|
143
|
-
// The matrix rows are pi / claude-code targets — the native-push branch (antigravity)
|
|
144
|
-
// is NOT exercised here (it is a SEPARATE table, covered by check-entwurf-v2-contract's
|
|
145
|
-
// NATIVE_PUSH round-trip + check-entwurf-v2-decider's branch scenarios). A tripwire so a
|
|
146
|
-
// future antigravity row that forgets to wire this fails loud instead of misrouting.
|
|
147
|
-
nativePushProbe: () => {
|
|
148
|
-
throw new Error("matrix: native-push branch must not be reached (no antigravity row wired)");
|
|
149
|
-
},
|
|
150
|
-
mailboxDir: "/fake/mailbox",
|
|
151
|
-
sessionsDir: "/fake/sessions",
|
|
152
|
-
};
|
|
153
|
-
return { deps, acquireCalls, releaseCalls, inspectCalls, probeCalls, mailboxCalls };
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
type LockClass = "none" | "held" | "mailbox-null" | "released" | "acquire-fail";
|
|
157
|
-
|
|
158
|
-
type Expect =
|
|
159
|
-
| { decision: "execute"; transport: EntwurfV2Transport; lock: LockClass }
|
|
160
|
-
// `diagnostic` names the KIND a reject must carry, not a boolean: two reject cells now
|
|
161
|
-
// carry machine-readable evidence, and the axis has to say which — a reject that carried
|
|
162
|
-
// the wrong diagnostic would pass a yes/no cell (#101 갭 C).
|
|
163
|
-
| { decision: "reject"; reason: string; lock: LockClass; diagnostic?: RejectDiagnostic["kind"] };
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Which IO seams the decider is ALLOWED to touch — the "어느 축을 만지면 안 되는지"
|
|
167
|
-
* axis (GPT D4-a review). A row name says "no inspect"; reachOf() + runRow make the
|
|
168
|
-
* machine enforce it instead of trusting the prose. DERIVED from the scenario, never
|
|
169
|
-
* hand-set, so a new row cannot drift from its declared target kind:
|
|
170
|
-
* pre-probe — rejected before any socket work: inspect 0, probe 0, mailbox 0
|
|
171
|
-
* (bad-target, pre-probe conflict, target-locked = lock fails first).
|
|
172
|
-
* unsupported — lock-free mailbox branch: the deliverability seam is consulted
|
|
173
|
-
* exactly once, NO socket inspect/probe: inspect 0, probe 0, mailbox 1.
|
|
174
|
-
* in-domain — locked socket branch: inspect ≥1, mailbox 0 (probe count varies by
|
|
175
|
-
* inspection kind, so it is not asserted here).
|
|
176
|
-
*/
|
|
177
|
-
type Reaches = "pre-probe" | "unsupported" | "in-domain";
|
|
178
|
-
|
|
179
|
-
function reachOf(s: ScenarioOpts): Reaches {
|
|
180
|
-
const id = s.resolution.identity;
|
|
181
|
-
if (!id) return "pre-probe"; // bad-target — no citizen
|
|
182
|
-
if (s.resolution.preProbeAddressConflict) return "pre-probe";
|
|
183
|
-
if (s.lock === "conflict") return "pre-probe"; // target-locked: lock fails before the socket
|
|
184
|
-
return isLivenessSupported(id.backend) ? "in-domain" : "unsupported";
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
interface Row {
|
|
188
|
-
name: string;
|
|
189
|
-
targetKind: string;
|
|
190
|
-
intent: EntwurfIntent;
|
|
191
|
-
scenario: ScenarioOpts;
|
|
192
|
-
expect: Expect;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const present = (i: TargetSocketInspection) => i;
|
|
196
|
-
|
|
197
|
-
// ── THE TABLE — every reachability + lock cell the 5d-5 claim covers ─────────
|
|
198
|
-
const ROWS: Row[] = [
|
|
199
|
-
{
|
|
200
|
-
name: "bad-target",
|
|
201
|
-
targetKind: "no citizen",
|
|
202
|
-
intent: "fire-and-forget",
|
|
203
|
-
scenario: { resolution: { identity: null, preProbeAddressConflict: false } },
|
|
204
|
-
expect: { decision: "reject", reason: "bad-target", lock: "none" },
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
name: "address-conflict (pre-probe)",
|
|
208
|
-
targetKind: "quarantined gid/socket",
|
|
209
|
-
intent: "fire-and-forget",
|
|
210
|
-
scenario: { resolution: { identity: identity("claude-code"), preProbeAddressConflict: true } },
|
|
211
|
-
expect: { decision: "reject", reason: "target-address-conflict", lock: "none" },
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
name: "target-locked",
|
|
215
|
-
targetKind: "pi, lock held by another",
|
|
216
|
-
intent: "fire-and-forget",
|
|
217
|
-
scenario: { resolution: { identity: identity("pi"), preProbeAddressConflict: false }, lock: "conflict" },
|
|
218
|
-
expect: { decision: "reject", reason: "target-locked", lock: "acquire-fail", diagnostic: "target-locked" },
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
name: "unsupported self-fetch active → meta-mailbox",
|
|
222
|
-
targetKind: "claude-code, active receiver",
|
|
223
|
-
intent: "fire-and-forget",
|
|
224
|
-
scenario: {
|
|
225
|
-
resolution: { identity: identity("claude-code"), preProbeAddressConflict: false },
|
|
226
|
-
mailboxDeliverable: true,
|
|
227
|
-
},
|
|
228
|
-
expect: { decision: "execute", transport: "meta-mailbox", lock: "mailbox-null" },
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
name: "unsupported self-fetch inactive → reject",
|
|
232
|
-
targetKind: "claude-code, inactive receiver",
|
|
233
|
-
intent: "fire-and-forget",
|
|
234
|
-
scenario: {
|
|
235
|
-
resolution: { identity: identity("claude-code"), preProbeAddressConflict: false },
|
|
236
|
-
mailboxDeliverable: false,
|
|
237
|
-
},
|
|
238
|
-
// #101 갭 C: an undeliverable mailbox reject carries the receiver axis that failed.
|
|
239
|
-
expect: { decision: "reject", reason: "mailbox-undeliverable", lock: "none", diagnostic: "mailbox-undeliverable" },
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
name: "in-domain live ff → control-socket",
|
|
243
|
-
targetKind: "pi, socket alive",
|
|
244
|
-
intent: "fire-and-forget",
|
|
245
|
-
scenario: {
|
|
246
|
-
resolution: { identity: identity("pi"), preProbeAddressConflict: false },
|
|
247
|
-
lock: "ok",
|
|
248
|
-
inspection: present({ kind: "socket-file", socketPath: "/fake/ctl/s.sock" }),
|
|
249
|
-
probe: "alive",
|
|
250
|
-
},
|
|
251
|
-
expect: { decision: "execute", transport: "control-socket", lock: "held" },
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
name: "in-domain ff dormant → reject",
|
|
255
|
-
targetKind: "pi, confirmed dormant, ff intent",
|
|
256
|
-
intent: "fire-and-forget",
|
|
257
|
-
scenario: {
|
|
258
|
-
resolution: { identity: identity("pi"), preProbeAddressConflict: false },
|
|
259
|
-
lock: "ok",
|
|
260
|
-
inspection: present({ kind: "absent", socketPath: "/fake/ctl/s.sock" }),
|
|
261
|
-
probe: "dead",
|
|
262
|
-
},
|
|
263
|
-
expect: { decision: "reject", reason: "dormant-fire-forget-unsupported", lock: "released" },
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
name: "in-domain indeterminate → reject",
|
|
267
|
-
targetKind: "pi, socket indeterminate (EACCES)",
|
|
268
|
-
intent: "fire-and-forget",
|
|
269
|
-
scenario: {
|
|
270
|
-
resolution: { identity: identity("pi"), preProbeAddressConflict: false },
|
|
271
|
-
lock: "ok",
|
|
272
|
-
inspection: present({ kind: "indeterminate", socketPath: "/fake/ctl/s.sock", error: "EACCES" }),
|
|
273
|
-
},
|
|
274
|
-
expect: { decision: "reject", reason: "indeterminate-no-spawn", lock: "released" },
|
|
275
|
-
},
|
|
276
|
-
{
|
|
277
|
-
name: "in-domain address-conflict under lock → reject (released)",
|
|
278
|
-
targetKind: "pi, socket symlink address-conflict",
|
|
279
|
-
intent: "fire-and-forget",
|
|
280
|
-
scenario: {
|
|
281
|
-
resolution: { identity: identity("pi"), preProbeAddressConflict: false },
|
|
282
|
-
lock: "ok",
|
|
283
|
-
inspection: present({ kind: "address-conflict", socketPath: "/fake/ctl/s.sock", reason: "symlink" }),
|
|
284
|
-
},
|
|
285
|
-
expect: { decision: "reject", reason: "target-address-conflict", lock: "released" },
|
|
286
|
-
},
|
|
287
|
-
];
|
|
288
|
-
|
|
289
|
-
function planTransport(d: DispatchDecision): EntwurfV2Transport | null {
|
|
290
|
-
return d.kind === "execute" ? (d.plan as ExecutionPlan).transport : null;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Verify a row against the REAL decideDispatch + the tracked lock calls.
|
|
294
|
-
async function runRow(row: Row): Promise<void> {
|
|
295
|
-
const t = mkDeps(row.scenario);
|
|
296
|
-
const d = await decideDispatch({ target: GID, intent: row.intent, message: "hi" }, t.deps);
|
|
297
|
-
const acq = t.acquireCalls.length;
|
|
298
|
-
const rel = t.releaseCalls.length;
|
|
299
|
-
|
|
300
|
-
// reach axis (GPT D4-a): the decider must touch ONLY the seams this target kind
|
|
301
|
-
// allows — make the "no inspect / no mailbox" prose machine-checked.
|
|
302
|
-
const reaches = reachOf(row.scenario);
|
|
303
|
-
const insp = t.inspectCalls.length;
|
|
304
|
-
const prb = t.probeCalls.length;
|
|
305
|
-
const mbx = t.mailboxCalls.length;
|
|
306
|
-
if (reaches === "pre-probe") {
|
|
307
|
-
ok(`${row.name}: pre-probe reach — no inspect, no probe, no mailbox`, insp === 0 && prb === 0 && mbx === 0);
|
|
308
|
-
} else if (reaches === "unsupported") {
|
|
309
|
-
ok(
|
|
310
|
-
`${row.name}: unsupported reach — deliverability seam ×1, no socket inspect/probe`,
|
|
311
|
-
mbx === 1 && insp === 0 && prb === 0,
|
|
312
|
-
);
|
|
313
|
-
} else {
|
|
314
|
-
ok(`${row.name}: in-domain reach — socket inspected, deliverability seam untouched`, insp >= 1 && mbx === 0);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (row.expect.decision === "execute") {
|
|
318
|
-
ok(`${row.name}: execute`, d.kind === "execute");
|
|
319
|
-
ok(`${row.name}: transport=${row.expect.transport}`, planTransport(d) === row.expect.transport);
|
|
320
|
-
const lockNull = d.kind === "execute" && d.lock === null;
|
|
321
|
-
if (row.expect.lock === "held") {
|
|
322
|
-
ok(`${row.name}: lock held (acquire 1, release 0, non-null)`, acq === 1 && rel === 0 && !lockNull);
|
|
323
|
-
} else {
|
|
324
|
-
// mailbox-null
|
|
325
|
-
ok(`${row.name}: lock-free meta-mailbox (acquire 0, lock null)`, acq === 0 && rel === 0 && lockNull);
|
|
326
|
-
}
|
|
327
|
-
} else {
|
|
328
|
-
ok(`${row.name}: reject`, d.kind === "reject");
|
|
329
|
-
ok(`${row.name}: reason=${row.expect.reason}`, d.kind === "reject" && d.receipt.reason === row.expect.reason);
|
|
330
|
-
ok(`${row.name}: no plan field`, !("plan" in d));
|
|
331
|
-
switch (row.expect.lock) {
|
|
332
|
-
case "none":
|
|
333
|
-
ok(`${row.name}: no lock (acquire 0, release 0)`, acq === 0 && rel === 0);
|
|
334
|
-
break;
|
|
335
|
-
case "acquire-fail":
|
|
336
|
-
ok(`${row.name}: acquire failed (acquire 1, release 0)`, acq === 1 && rel === 0);
|
|
337
|
-
break;
|
|
338
|
-
case "released":
|
|
339
|
-
ok(`${row.name}: lock acquired then released ×1 (acquire 1, release 1)`, acq === 1 && rel === 1);
|
|
340
|
-
break;
|
|
341
|
-
default:
|
|
342
|
-
assert.fail(`${row.name}: reject row cannot expect lock=${row.expect.lock}`);
|
|
343
|
-
}
|
|
344
|
-
if (row.expect.diagnostic) {
|
|
345
|
-
ok(
|
|
346
|
-
`${row.name}: carries the ${row.expect.diagnostic} diagnostic`,
|
|
347
|
-
d.kind === "reject" && d.diagnostic?.kind === row.expect.diagnostic,
|
|
348
|
-
);
|
|
349
|
-
} else {
|
|
350
|
-
ok(`${row.name}: no diagnostic`, d.kind === "reject" && d.diagnostic === undefined);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
async function main(): Promise<void> {
|
|
356
|
-
console.log("[check-entwurf-v2-matrix] 5d-5 (a) reachability + lock SSOT table\n");
|
|
357
|
-
for (const row of ROWS) {
|
|
358
|
-
console.log(`── ${row.name} [${row.targetKind}]`);
|
|
359
|
-
await runRow(row);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// ── COVERAGE: the table must span every matrix-owned transport + lock class + ──
|
|
363
|
-
// pre-probe reject, or the "matrix is closed" claim is a lie. A dropped decider cell
|
|
364
|
-
// makes one of these sets shrink → fail (not a silent green). NOTE: native-push is NOT
|
|
365
|
-
// a matrix-owned transport — it is a SEPARATE NATIVE_PUSH_DISPATCH_TABLE (check-entwurf-
|
|
366
|
-
// v2-contract round-trip + check-entwurf-v2-decider branch), so it is deliberately absent
|
|
367
|
-
// from this coverage set (adding an antigravity row would blur the matrix's lock-class /
|
|
368
|
-
// transport semantics — GPT R8).
|
|
369
|
-
console.log("\n── coverage (table completeness)");
|
|
370
|
-
const transports = new Set<string>();
|
|
371
|
-
const lockClasses = new Set<LockClass>();
|
|
372
|
-
const rejectReasons = new Set<string>();
|
|
373
|
-
for (const row of ROWS) {
|
|
374
|
-
lockClasses.add(row.expect.lock);
|
|
375
|
-
if (row.expect.decision === "execute") transports.add(row.expect.transport);
|
|
376
|
-
else rejectReasons.add(row.expect.reason);
|
|
377
|
-
}
|
|
378
|
-
for (const tr of ["control-socket", "meta-mailbox"]) {
|
|
379
|
-
ok(`coverage: transport "${tr}" exercised`, transports.has(tr));
|
|
380
|
-
}
|
|
381
|
-
for (const lc of ["none", "held", "mailbox-null", "released", "acquire-fail"] as LockClass[]) {
|
|
382
|
-
ok(`coverage: lock class "${lc}" exercised`, lockClasses.has(lc));
|
|
383
|
-
}
|
|
384
|
-
for (const rr of [
|
|
385
|
-
"bad-target",
|
|
386
|
-
"target-address-conflict",
|
|
387
|
-
"target-locked",
|
|
388
|
-
"mailbox-undeliverable",
|
|
389
|
-
"dormant-fire-forget-unsupported",
|
|
390
|
-
"indeterminate-no-spawn",
|
|
391
|
-
]) {
|
|
392
|
-
ok(`coverage: reject reason "${rr}" in table`, rejectReasons.has(rr));
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
console.log(`\n[check-entwurf-v2-matrix] ${passed} assertions ok`);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
main().catch((err) => {
|
|
399
|
-
console.error(err);
|
|
400
|
-
process.exit(1);
|
|
401
|
-
});
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* check-entwurf-v2-native-push — deterministic gate for the native-push SEND hand
|
|
3
|
-
* (봉인 3/4), the executor half of the native-push rail. This is where the 1-shot retry
|
|
4
|
-
* lives (moved out of the adapter leaf per the review): the decider planted a fresh route,
|
|
5
|
-
* the hand sends over it, and on failure re-probes ONCE and re-sends.
|
|
6
|
-
*
|
|
7
|
-
* Proves (deliverViaNativePush, fake adapter):
|
|
8
|
-
* - success first try → {retried:false}, ONE send, ZERO re-probe (the planted route is used).
|
|
9
|
-
* - fail → re-probe alive → re-send success → {retried:true}, TWO sends, ONE re-probe, and
|
|
10
|
-
* the second send uses the RE-DISCOVERED route (not the stale planted one).
|
|
11
|
-
* - fail → re-probe alive → re-send FAIL → THROWS (fail-loud, no third attempt).
|
|
12
|
-
* - fail → re-probe dead/indeterminate → THROWS (not retried), NO second send.
|
|
13
|
-
* Proves (makeNativePushSend):
|
|
14
|
-
* - resolves the adapter from plan.backend, delivers via deliverViaNativePush, and IGNORES
|
|
15
|
-
* the lock (native-push is lock-free, 봉인 4).
|
|
16
|
-
*
|
|
17
|
-
* Pure; no backend, no socket, no real process.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import assert from "node:assert/strict";
|
|
21
|
-
import type { LockClaim } from "../pi-extensions/lib/entwurf-v2-lock.ts";
|
|
22
|
-
import type { NativePushPlan } from "../pi-extensions/lib/entwurf-v2-native-push.ts";
|
|
23
|
-
import { deliverViaNativePush, makeNativePushSend } from "../pi-extensions/lib/entwurf-v2-native-push.ts";
|
|
24
|
-
import type {
|
|
25
|
-
NativePushAdapter,
|
|
26
|
-
NativePushProbeResult,
|
|
27
|
-
NativePushRoute,
|
|
28
|
-
} from "../pi-extensions/lib/native-push/adapter.ts";
|
|
29
|
-
|
|
30
|
-
let passed = 0;
|
|
31
|
-
function ok(label: string, cond: boolean): void {
|
|
32
|
-
assert.ok(cond, label);
|
|
33
|
-
console.log(` ok ${label}`);
|
|
34
|
-
passed++;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const CONV = "conv-xyz";
|
|
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
|
-
}
|
|
43
|
-
|
|
44
|
-
interface SendCall {
|
|
45
|
-
route: NativePushRoute;
|
|
46
|
-
nativeSessionId: string;
|
|
47
|
-
content: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
interface FakeAdapterConfig {
|
|
51
|
-
/** probe() results consumed in order (each re-probe pops the next). */
|
|
52
|
-
probes: NativePushProbeResult[];
|
|
53
|
-
/** 0-based indices of send() calls that THROW. */
|
|
54
|
-
sendFailAt?: number[];
|
|
55
|
-
retriable?: boolean;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function makeFakeAdapter(config: FakeAdapterConfig): {
|
|
59
|
-
adapter: NativePushAdapter;
|
|
60
|
-
sends: SendCall[];
|
|
61
|
-
probeCount: () => number;
|
|
62
|
-
} {
|
|
63
|
-
let probeIdx = 0;
|
|
64
|
-
let sendIdx = 0;
|
|
65
|
-
const sends: SendCall[] = [];
|
|
66
|
-
const adapter: NativePushAdapter = {
|
|
67
|
-
id: "antigravity",
|
|
68
|
-
retriable: config.retriable ?? true,
|
|
69
|
-
async probe() {
|
|
70
|
-
const p = config.probes[probeIdx] ?? { status: "dead", reason: "fake: probes exhausted" };
|
|
71
|
-
probeIdx++;
|
|
72
|
-
return p;
|
|
73
|
-
},
|
|
74
|
-
async send(route, nativeSessionId, content) {
|
|
75
|
-
const i = sendIdx++;
|
|
76
|
-
sends.push({ route, nativeSessionId, content });
|
|
77
|
-
if (config.sendFailAt?.includes(i)) throw new Error(`fake send fail #${i}`);
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
return { adapter, sends, probeCount: () => probeIdx };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function main(): Promise<void> {
|
|
84
|
-
// ── success first try: ONE send over the planted route, ZERO re-probe ────────
|
|
85
|
-
{
|
|
86
|
-
const { adapter, sends, probeCount } = makeFakeAdapter({ probes: [] });
|
|
87
|
-
const r = await deliverViaNativePush(adapter, PLANTED, CONV, "hi");
|
|
88
|
-
ok("success: retried:false", r.success === true && r.retried === false);
|
|
89
|
-
ok("success: exactly ONE send", sends.length === 1);
|
|
90
|
-
ok("success: send used the PLANTED route (no re-probe)", agyAddress(sends[0]?.route) === "127.0.0.1:5600");
|
|
91
|
-
ok("success: ZERO re-probe", probeCount() === 0);
|
|
92
|
-
}
|
|
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
|
-
|
|
106
|
-
// ── fail → re-probe alive → re-send success: retried, fresh route ────────────
|
|
107
|
-
{
|
|
108
|
-
const { adapter, sends, probeCount } = makeFakeAdapter({
|
|
109
|
-
probes: [{ status: "alive", route: { backend: "antigravity", lsAddress: "127.0.0.1:5601" } }],
|
|
110
|
-
sendFailAt: [0],
|
|
111
|
-
});
|
|
112
|
-
const r = await deliverViaNativePush(adapter, PLANTED, CONV, "hi");
|
|
113
|
-
ok("retry: retried:true", r.success === true && r.retried === true);
|
|
114
|
-
ok("retry: TWO sends (initial + one retry)", sends.length === 2);
|
|
115
|
-
ok("retry: exactly ONE re-probe", probeCount() === 1);
|
|
116
|
-
ok("retry: 1st send used the planted route", agyAddress(sends[0]?.route) === "127.0.0.1:5600");
|
|
117
|
-
ok(
|
|
118
|
-
"retry: 2nd send used the RE-DISCOVERED route (not the stale one)",
|
|
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,
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// ── fail → re-probe alive → re-send FAIL: throws (fail-loud, no 3rd attempt) ──
|
|
134
|
-
{
|
|
135
|
-
const { adapter, sends } = makeFakeAdapter({
|
|
136
|
-
probes: [{ status: "alive", route: { backend: "antigravity", lsAddress: "127.0.0.1:5601" } }],
|
|
137
|
-
sendFailAt: [0, 1],
|
|
138
|
-
});
|
|
139
|
-
let threw = false;
|
|
140
|
-
try {
|
|
141
|
-
await deliverViaNativePush(adapter, PLANTED, CONV, "hi");
|
|
142
|
-
} catch {
|
|
143
|
-
threw = true;
|
|
144
|
-
}
|
|
145
|
-
ok("double-fail: THROWS (fail-loud)", threw);
|
|
146
|
-
ok("double-fail: exactly TWO sends, no third attempt", sends.length === 2);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// ── fail → re-probe dead: throws (not retried), NO second send ───────────────
|
|
150
|
-
{
|
|
151
|
-
const { adapter, sends, probeCount } = makeFakeAdapter({
|
|
152
|
-
probes: [{ status: "dead", reason: "host gone" }],
|
|
153
|
-
sendFailAt: [0],
|
|
154
|
-
});
|
|
155
|
-
let msg = "";
|
|
156
|
-
try {
|
|
157
|
-
await deliverViaNativePush(adapter, PLANTED, CONV, "hi");
|
|
158
|
-
} catch (err) {
|
|
159
|
-
msg = (err as Error).message;
|
|
160
|
-
}
|
|
161
|
-
ok("reprobe-dead: THROWS", msg.length > 0);
|
|
162
|
-
ok("reprobe-dead: error names the dead re-probe + 'not retried'", /dead/.test(msg) && /not retried/.test(msg));
|
|
163
|
-
ok("reprobe-dead: exactly ONE send (no re-send into a dead conversation)", sends.length === 1);
|
|
164
|
-
ok("reprobe-dead: re-probe was attempted once", probeCount() === 1);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// ── fail → re-probe indeterminate: throws (not retried) ──────────────────────
|
|
168
|
-
{
|
|
169
|
-
const { adapter, sends } = makeFakeAdapter({
|
|
170
|
-
probes: [{ status: "indeterminate", reason: "no port" }],
|
|
171
|
-
sendFailAt: [0],
|
|
172
|
-
});
|
|
173
|
-
let threw = false;
|
|
174
|
-
try {
|
|
175
|
-
await deliverViaNativePush(adapter, PLANTED, CONV, "hi");
|
|
176
|
-
} catch {
|
|
177
|
-
threw = true;
|
|
178
|
-
}
|
|
179
|
-
ok("reprobe-indeterminate: THROWS (never coerced to a retry)", threw);
|
|
180
|
-
ok("reprobe-indeterminate: exactly ONE send", sends.length === 1);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// ── makeNativePushSend: resolves adapter from plan.backend, ignores lock ─────
|
|
184
|
-
{
|
|
185
|
-
const plan: NativePushPlan = {
|
|
186
|
-
transport: "native-push",
|
|
187
|
-
action: "send",
|
|
188
|
-
targetGardenId: "20260704T000000-abcdef",
|
|
189
|
-
backend: "antigravity",
|
|
190
|
-
nativeSessionId: CONV,
|
|
191
|
-
route: PLANTED,
|
|
192
|
-
wantsReply: false,
|
|
193
|
-
message: "payload",
|
|
194
|
-
};
|
|
195
|
-
const { adapter, sends } = makeFakeAdapter({ probes: [] });
|
|
196
|
-
const resolvedBackends: string[] = [];
|
|
197
|
-
const send = makeNativePushSend({
|
|
198
|
-
resolveAdapter: (backend) => {
|
|
199
|
-
resolvedBackends.push(backend);
|
|
200
|
-
return adapter;
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
// pass a NON-null lock to prove it is ignored (lock-free rail).
|
|
204
|
-
const bogusLock = { gardenId: "x" } as unknown as LockClaim;
|
|
205
|
-
const r = await send(plan, bogusLock);
|
|
206
|
-
ok("makeNativePushSend: delivered success", r.success === true && r.retried === false);
|
|
207
|
-
ok(
|
|
208
|
-
"makeNativePushSend: resolved the adapter from plan.backend",
|
|
209
|
-
resolvedBackends.length === 1 && resolvedBackends[0] === "antigravity",
|
|
210
|
-
);
|
|
211
|
-
ok(
|
|
212
|
-
"makeNativePushSend: sent the plan message over the plan route",
|
|
213
|
-
sends[0]?.content === "payload" && agyAddress(sends[0]?.route) === "127.0.0.1:5600",
|
|
214
|
-
);
|
|
215
|
-
ok("makeNativePushSend: lock IGNORED (lock-free — a bogus lock did not break delivery)", sends.length === 1);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
console.log(`\ncheck-entwurf-v2-native-push: ${passed} assertions passed`);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
void main();
|