@junghanacs/entwurf 0.12.0 → 0.12.2
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/CHANGELOG.md +40 -0
- package/README.md +88 -28
- package/docs/setup-clean-host.md +117 -219
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +454 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +111 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-core.js +1683 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-deliverability.js +76 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +121 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-facts.js +155 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +119 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-preflight.js +160 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-resume-args.js +63 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +81 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +290 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +254 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +365 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-mailbox.js +64 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +218 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-release.js +108 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-resume-marker.js +33 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +116 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send-fallback.js +125 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +184 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn-production.js +237 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn.js +216 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +164 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +66 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +1502 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/session-id.js +50 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/socket-discovery.js +259 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/socket-probe.js +81 -0
- package/mcp/entwurf-bridge/dist/protocol.js +29 -0
- package/mcp/entwurf-bridge/start.sh +49 -7
- package/mcp/entwurf-bridge/test.sh +12 -3
- package/mcp/entwurf-bridge/tsconfig.build.json +42 -0
- package/package.json +30 -9
- package/pi/meta-bridge/.claude-plugin/marketplace.json +0 -1
- package/pi-extensions/lib/entwurf-v2-contract-schema.ts +101 -0
- package/pi-extensions/lib/entwurf-v2-contract.ts +10 -78
- package/pi-extensions/lib/entwurf-v2-decider.ts +6 -2
- package/pi-extensions/lib/entwurf-v2-production.ts +26 -4
- package/run.sh +150 -15
- package/scripts/check-entwurf-bridge-pi-free.ts +146 -0
- package/scripts/check-entwurf-v2-contract.ts +6 -4
- package/scripts/check-meta-manifest-schema.py +145 -0
- package/scripts/meta-bridge-install.sh +17 -3
- package/scripts/meta-bridge-state.py +37 -10
- package/scripts/smoke-acp-bundled-mcp-live.ts +13 -2
- package/scripts/smoke-acp-carrier-augment-live.ts +35 -19
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meta-bridge-local",
|
|
3
3
|
"owner": { "name": "junghan0611" },
|
|
4
|
-
"description": "Local marketplace for the entwurf garden-native meta-bridge. Shipped in-repo (repo-stable, NOT /tmp — an ephemeral source breaks `claude plugin marketplace update`). The installer (./run.sh install-meta-bridge) assembles a self-contained copy under .assembled/ (entry + lib + node-path-baked hooks) and runs `marketplace add` + `install --scope user`.",
|
|
5
4
|
"plugins": [
|
|
6
5
|
{
|
|
7
6
|
"name": "entwurf-meta-receive",
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* entwurf-v2-contract-schema — the pi-side TypeBox REPRESENTATION of the frozen
|
|
3
|
+
* `entwurf_v2` contract. This module exists ONLY to carry the pi-ai TypeBox
|
|
4
|
+
* schema builders (`StringEnum`, `Type`) out of the pi-free core
|
|
5
|
+
* (`entwurf-v2-contract.ts`); the constants/types/decision logic live there.
|
|
6
|
+
*
|
|
7
|
+
* ⚠️ pi LANE ONLY — the MCP bridge (`mcp/entwurf-bridge/src/index.ts`) MUST NOT
|
|
8
|
+
* import this module. It value-imports `@earendil-works/pi-ai`, so reaching it
|
|
9
|
+
* from the bridge boot closure would re-couple the harness-neutral meta-bridge to
|
|
10
|
+
* pi (the `check-entwurf-bridge-pi-free` gate fails if it does). The bridge needs
|
|
11
|
+
* the contract CONSTANTS + `resolveDispatch` (pi-free core), never these schemas.
|
|
12
|
+
* Consumers: the pi MCP-tool param surface and `check-entwurf-v2-contract`.
|
|
13
|
+
*
|
|
14
|
+
* StringEnum (typebox 1.x) inside Type.Object (typebox 0.34) — the same mix the
|
|
15
|
+
* existing entwurf tools use. The logic types in the core are hand-written unions,
|
|
16
|
+
* NOT `Static<>` inferences, so the 0.34/1.x widening caveat does not touch them;
|
|
17
|
+
* `check-entwurf-v2-contract` keeps these schemas and the core types in lockstep.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { StringEnum, Type } from "@earendil-works/pi-ai";
|
|
21
|
+
import {
|
|
22
|
+
ENTWURF_INTENTS,
|
|
23
|
+
ENTWURF_V2_ACTIONS,
|
|
24
|
+
ENTWURF_V2_MODES,
|
|
25
|
+
ENTWURF_V2_OWNERSHIPS,
|
|
26
|
+
ENTWURF_V2_REJECT_REASONS,
|
|
27
|
+
ENTWURF_V2_TRANSPORTS,
|
|
28
|
+
FACT_LIVENESSES,
|
|
29
|
+
} from "./entwurf-v2-contract.ts";
|
|
30
|
+
import { SESSION_ID_RE } from "./session-id.js";
|
|
31
|
+
|
|
32
|
+
export const EntwurfV2InputSchema = Type.Object(
|
|
33
|
+
{
|
|
34
|
+
// R2/F6 executable: the garden-id shape is enforced by pattern, not prose —
|
|
35
|
+
// a malformed/typo gid fails the schema (→ bad-target) and can never reach a
|
|
36
|
+
// spawn. SSOT regex = SESSION_ID_RE (pi-extensions/lib/session-id.js).
|
|
37
|
+
target: Type.String({
|
|
38
|
+
pattern: SESSION_ID_RE.source,
|
|
39
|
+
description:
|
|
40
|
+
"garden-id of an EXISTING citizen (pattern-enforced). spawn-new is out of v2 scope (legacy entwurf keeps it); a malformed/typo gid is bad-target.",
|
|
41
|
+
}),
|
|
42
|
+
intent: StringEnum(ENTWURF_INTENTS, {
|
|
43
|
+
description:
|
|
44
|
+
"caller's declared outcome contract (F1): fire-and-forget = ack only, owned-outcome = caller owns completion.",
|
|
45
|
+
}),
|
|
46
|
+
mode: Type.Optional(
|
|
47
|
+
StringEnum(ENTWURF_V2_MODES, {
|
|
48
|
+
description:
|
|
49
|
+
"delivery mode (steer = interrupt current turn, follow_up = queue) — NOT the ownership axis (F1) nor liveness routing. MEANINGLESS on the meta-mailbox transport (F-mailbox): a mailbox ack is enqueue+doorbell, not a turn injection, so steer/follow_up does not apply when the verdict transport is meta-mailbox.",
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
52
|
+
wantsReply: Type.Optional(
|
|
53
|
+
Type.Boolean({
|
|
54
|
+
description: "conversation etiquette only — NOT ownership; never triggers an auto-send (Q2).",
|
|
55
|
+
}),
|
|
56
|
+
),
|
|
57
|
+
// `additionalProperties: false` — a frozen contract input is exact; an unknown
|
|
58
|
+
// key is a caller error, not silently ignored.
|
|
59
|
+
},
|
|
60
|
+
{ additionalProperties: false },
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// Receipt = a DISCRIMINATED union on `ok` (R3/F6) — NOT one flat object with
|
|
64
|
+
// optionals. Each branch is EXACT (`additionalProperties: false`): without it,
|
|
65
|
+
// JSON Schema's default admits extra keys, so an illegal receipt like
|
|
66
|
+
// {ok:true, ..., reason:"bad-target"} would validate against the success branch.
|
|
67
|
+
// With it, success carries action/transport/ownership and rejects a stray reason;
|
|
68
|
+
// reject carries reason and rejects any allow facet — the branches are mutually
|
|
69
|
+
// exclusive at the schema level, not merely by declared-property convention.
|
|
70
|
+
export const EntwurfV2ReceiptSuccessSchema = Type.Object(
|
|
71
|
+
{
|
|
72
|
+
ok: Type.Literal(true),
|
|
73
|
+
action: StringEnum(ENTWURF_V2_ACTIONS),
|
|
74
|
+
transport: StringEnum(ENTWURF_V2_TRANSPORTS),
|
|
75
|
+
ownership: StringEnum(ENTWURF_V2_OWNERSHIPS),
|
|
76
|
+
observedLiveness: StringEnum(FACT_LIVENESSES, {
|
|
77
|
+
description: "the 4-value fact liveness the verdict was computed from (R1/R3).",
|
|
78
|
+
}),
|
|
79
|
+
},
|
|
80
|
+
{ additionalProperties: false },
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
export const EntwurfV2ReceiptRejectSchema = Type.Object(
|
|
84
|
+
{
|
|
85
|
+
ok: Type.Literal(false),
|
|
86
|
+
reason: StringEnum(ENTWURF_V2_REJECT_REASONS),
|
|
87
|
+
// ?6: required-nullable, NOT optional — a reject branch ALWAYS carries the
|
|
88
|
+
// key, and it is `null` for the pre-probe rejects (PRE_PROBE_REJECT_REASONS)
|
|
89
|
+
// and a real FactLiveness otherwise. Optional would lose the "key always
|
|
90
|
+
// present, value may be null" shape and weaken the discriminated union; the
|
|
91
|
+
// reason-dependent null/non-null rule is enforced semantically (the gate's
|
|
92
|
+
// rejectObservedLivenessWellFormed fixture), not by this blanket union.
|
|
93
|
+
observedLiveness: Type.Union([StringEnum(FACT_LIVENESSES), Type.Null()], {
|
|
94
|
+
description:
|
|
95
|
+
"the 4-value fact liveness the reject was computed from (R1/R3); null for the pre-probe rejects (bad-target / target-locked / target-address-conflict) where no probe ran.",
|
|
96
|
+
}),
|
|
97
|
+
},
|
|
98
|
+
{ additionalProperties: false },
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
export const EntwurfV2ReceiptSchema = Type.Union([EntwurfV2ReceiptSuccessSchema, EntwurfV2ReceiptRejectSchema]);
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* entwurf-v2-contract — the FROZEN contract surface for the unified `entwurf_v2`
|
|
3
|
-
* verb (0.11 Stage 0 step 4-pre / 동결결정 10). PURE
|
|
3
|
+
* verb (0.11 Stage 0 step 4-pre / 동결결정 10). PURE pi-FREE core: the
|
|
4
4
|
* intent×liveness decision table + the reject taxonomy + a pure resolver.
|
|
5
5
|
* NO runtime dispatch, NO spawn/send, NO I/O — step 5 wires this to transports.
|
|
6
|
+
* The pi-ai TypeBox REPRESENTATION of this contract lives in the separate
|
|
7
|
+
* `entwurf-v2-contract-schema.ts` (0.12.1 B-1) so this module — which the
|
|
8
|
+
* harness-neutral MCP bridge reaches at boot — carries no pi dependency.
|
|
6
9
|
*
|
|
7
10
|
* Why a frozen contract BEFORE the fact-provider (step 4): with the legacy
|
|
8
11
|
* 3-verb surface (`entwurf`/`entwurf_resume`/`entwurf_send`) still live, building
|
|
@@ -41,8 +44,6 @@
|
|
|
41
44
|
* not prose.
|
|
42
45
|
*/
|
|
43
46
|
|
|
44
|
-
import { StringEnum, Type } from "@earendil-works/pi-ai";
|
|
45
|
-
import { SESSION_ID_RE } from "./session-id.js";
|
|
46
47
|
import type { SocketLiveness } from "./socket-probe.ts";
|
|
47
48
|
|
|
48
49
|
// ── Caller-declared intent (F1) ────────────────────────────────────────────
|
|
@@ -350,78 +351,9 @@ export function resolveDispatch(
|
|
|
350
351
|
};
|
|
351
352
|
}
|
|
352
353
|
|
|
353
|
-
// ── TypeBox schemas
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
|
|
359
|
-
{
|
|
360
|
-
// R2/F6 executable: the garden-id shape is enforced by pattern, not prose —
|
|
361
|
-
// a malformed/typo gid fails the schema (→ bad-target) and can never reach a
|
|
362
|
-
// spawn. SSOT regex = SESSION_ID_RE (pi-extensions/lib/session-id.js).
|
|
363
|
-
target: Type.String({
|
|
364
|
-
pattern: SESSION_ID_RE.source,
|
|
365
|
-
description:
|
|
366
|
-
"garden-id of an EXISTING citizen (pattern-enforced). spawn-new is out of v2 scope (legacy entwurf keeps it); a malformed/typo gid is bad-target.",
|
|
367
|
-
}),
|
|
368
|
-
intent: StringEnum(ENTWURF_INTENTS, {
|
|
369
|
-
description:
|
|
370
|
-
"caller's declared outcome contract (F1): fire-and-forget = ack only, owned-outcome = caller owns completion.",
|
|
371
|
-
}),
|
|
372
|
-
mode: Type.Optional(
|
|
373
|
-
StringEnum(ENTWURF_V2_MODES, {
|
|
374
|
-
description:
|
|
375
|
-
"delivery mode (steer = interrupt current turn, follow_up = queue) — NOT the ownership axis (F1) nor liveness routing. MEANINGLESS on the meta-mailbox transport (F-mailbox): a mailbox ack is enqueue+doorbell, not a turn injection, so steer/follow_up does not apply when the verdict transport is meta-mailbox.",
|
|
376
|
-
}),
|
|
377
|
-
),
|
|
378
|
-
wantsReply: Type.Optional(
|
|
379
|
-
Type.Boolean({
|
|
380
|
-
description: "conversation etiquette only — NOT ownership; never triggers an auto-send (Q2).",
|
|
381
|
-
}),
|
|
382
|
-
),
|
|
383
|
-
// `additionalProperties: false` — a frozen contract input is exact; an unknown
|
|
384
|
-
// key is a caller error, not silently ignored.
|
|
385
|
-
},
|
|
386
|
-
{ additionalProperties: false },
|
|
387
|
-
);
|
|
388
|
-
|
|
389
|
-
// Receipt = a DISCRIMINATED union on `ok` (R3/F6) — NOT one flat object with
|
|
390
|
-
// optionals. Each branch is EXACT (`additionalProperties: false`): without it,
|
|
391
|
-
// JSON Schema's default admits extra keys, so an illegal receipt like
|
|
392
|
-
// {ok:true, ..., reason:"bad-target"} would validate against the success branch.
|
|
393
|
-
// With it, success carries action/transport/ownership and rejects a stray reason;
|
|
394
|
-
// reject carries reason and rejects any allow facet — the branches are mutually
|
|
395
|
-
// exclusive at the schema level, not merely by declared-property convention.
|
|
396
|
-
export const EntwurfV2ReceiptSuccessSchema = Type.Object(
|
|
397
|
-
{
|
|
398
|
-
ok: Type.Literal(true),
|
|
399
|
-
action: StringEnum(ENTWURF_V2_ACTIONS),
|
|
400
|
-
transport: StringEnum(ENTWURF_V2_TRANSPORTS),
|
|
401
|
-
ownership: StringEnum(ENTWURF_V2_OWNERSHIPS),
|
|
402
|
-
observedLiveness: StringEnum(FACT_LIVENESSES, {
|
|
403
|
-
description: "the 4-value fact liveness the verdict was computed from (R1/R3).",
|
|
404
|
-
}),
|
|
405
|
-
},
|
|
406
|
-
{ additionalProperties: false },
|
|
407
|
-
);
|
|
408
|
-
|
|
409
|
-
export const EntwurfV2ReceiptRejectSchema = Type.Object(
|
|
410
|
-
{
|
|
411
|
-
ok: Type.Literal(false),
|
|
412
|
-
reason: StringEnum(ENTWURF_V2_REJECT_REASONS),
|
|
413
|
-
// ?6: required-nullable, NOT optional — a reject branch ALWAYS carries the
|
|
414
|
-
// key, and it is `null` for the pre-probe rejects (PRE_PROBE_REJECT_REASONS)
|
|
415
|
-
// and a real FactLiveness otherwise. Optional would lose the "key always
|
|
416
|
-
// present, value may be null" shape and weaken the discriminated union; the
|
|
417
|
-
// reason-dependent null/non-null rule is enforced semantically (the gate's
|
|
418
|
-
// rejectObservedLivenessWellFormed fixture), not by this blanket union.
|
|
419
|
-
observedLiveness: Type.Union([StringEnum(FACT_LIVENESSES), Type.Null()], {
|
|
420
|
-
description:
|
|
421
|
-
"the 4-value fact liveness the reject was computed from (R1/R3); null for the pre-probe rejects (bad-target / target-locked / target-address-conflict) where no probe ran.",
|
|
422
|
-
}),
|
|
423
|
-
},
|
|
424
|
-
{ additionalProperties: false },
|
|
425
|
-
);
|
|
426
|
-
|
|
427
|
-
export const EntwurfV2ReceiptSchema = Type.Union([EntwurfV2ReceiptSuccessSchema, EntwurfV2ReceiptRejectSchema]);
|
|
354
|
+
// ── TypeBox schemas ────────────────────────────────────────────────────────
|
|
355
|
+
// MOVED to `entwurf-v2-contract-schema.ts` (0.12.1 B-1): the pi-ai TypeBox
|
|
356
|
+
// builders (StringEnum/Type) are a pi-lane dependency, so they cannot live in
|
|
357
|
+
// this pi-free core — the MCP bridge reaches this module at boot and must stay
|
|
358
|
+
// harness-neutral (check-entwurf-bridge-pi-free). The schemas import the
|
|
359
|
+
// constants/types above; pi-side consumers import the schemas from there.
|
|
@@ -195,7 +195,11 @@ export interface DispatchDeciderDeps {
|
|
|
195
195
|
releaseLock: (claim: LockClaim) => unknown;
|
|
196
196
|
inspectSocket: (gardenId: string) => Promise<TargetSocketInspection>;
|
|
197
197
|
probeSocket: (socketPath: string) => Promise<SocketLiveness>;
|
|
198
|
-
|
|
198
|
+
// MaybePromise (0.12.1 B-2): production lazy-imports the pi-coding-agent-backed
|
|
199
|
+
// preflight via `await import()` so the harness-neutral bridge boots pi-free;
|
|
200
|
+
// only the owned-outcome resume branch (below) awaits it. Sync test fakes that
|
|
201
|
+
// return a plain PreflightOutcome still satisfy this.
|
|
202
|
+
preflightForCwd: (cwd: string) => PreflightOutcome | Promise<PreflightOutcome>;
|
|
199
203
|
/**
|
|
200
204
|
* SE-2 slice 2d-3: the REQUIRED mailbox-deliverability seam (no default). The decider
|
|
201
205
|
* does NOT judge deliverability itself — it asks this injected fn, which combines the
|
|
@@ -392,7 +396,7 @@ async function decideInDomain(
|
|
|
392
396
|
// 1B: preflight runs ONLY here (the sole branch that launches a child into a
|
|
393
397
|
// target cwd). deny → nonce-owned release → untrusted-fail-fast, with the
|
|
394
398
|
// honest measured liveness (dormant = the `dead` we just probed).
|
|
395
|
-
const outcome = deps.preflightForCwd(resume.cwd);
|
|
399
|
+
const outcome = await deps.preflightForCwd(resume.cwd);
|
|
396
400
|
if (outcome.kind === "deny") {
|
|
397
401
|
return rejectAfterRelease(makeRejectReceipt("untrusted-fail-fast", liveness));
|
|
398
402
|
}
|
|
@@ -39,7 +39,12 @@ import {
|
|
|
39
39
|
receiverMarkerMatchesIdentity,
|
|
40
40
|
} from "./entwurf-deliverability.ts";
|
|
41
41
|
import { isNonPiGardenIdSocketConflict } from "./entwurf-facts.ts";
|
|
42
|
-
|
|
42
|
+
// 0.12.1 B-2: TYPE-ONLY import — `entwurf-preflight.ts` value-imports
|
|
43
|
+
// `@earendil-works/pi-coding-agent` (ProjectTrustStore), so a static value-import
|
|
44
|
+
// here would re-couple the harness-neutral MCP bridge to pi at boot
|
|
45
|
+
// (check-entwurf-bridge-pi-free). The real preflight is reached ONLY via the lazy
|
|
46
|
+
// `await import()` in `lazyProductionPreflight`, on the owned-outcome resume branch.
|
|
47
|
+
import type { PreflightInput, PreflightOutcome } from "./entwurf-preflight.ts";
|
|
43
48
|
import { isLivenessSupported } from "./entwurf-v2-contract.ts";
|
|
44
49
|
import {
|
|
45
50
|
type DispatchDeciderDeps,
|
|
@@ -109,7 +114,10 @@ export interface ProductionEntwurfV2Seams {
|
|
|
109
114
|
releaseLock: (claim: LockClaim, deps: { dir?: string }) => unknown;
|
|
110
115
|
inspectSocket: (gid: string, dir: string) => Promise<TargetSocketInspection>;
|
|
111
116
|
probeSocket: (socketPath: string) => Promise<SocketLiveness>;
|
|
112
|
-
|
|
117
|
+
// MaybePromise (0.12.1 B-2): the production default is the lazy wrapper, which
|
|
118
|
+
// `await import()`s the pi-coding-agent-backed preflight only on a resume verdict.
|
|
119
|
+
// A deterministic gate may still inject a sync spy returning a plain PreflightOutcome.
|
|
120
|
+
preflight: (input: PreflightInput) => PreflightOutcome | Promise<PreflightOutcome>;
|
|
113
121
|
classifyConnect: (code: string | undefined) => "dead" | "indeterminate";
|
|
114
122
|
sendRpc: (socketPath: string, command: RpcCommand, options?: RpcClientOptions) => Promise<{ response: RpcResponse }>;
|
|
115
123
|
enqueue: (opts: EnqueueMetaMessageOptions) => EnqueueMetaMessageResult;
|
|
@@ -138,6 +146,20 @@ export interface ProductionEntwurfV2Opts {
|
|
|
138
146
|
seams?: Partial<ProductionEntwurfV2Seams>;
|
|
139
147
|
}
|
|
140
148
|
|
|
149
|
+
/**
|
|
150
|
+
* 0.12.1 B-2: the production `preflight` seam default. preflight value-imports
|
|
151
|
+
* `@earendil-works/pi-coding-agent` (ProjectTrustStore), so importing it eagerly
|
|
152
|
+
* would pull pi into the harness-neutral MCP bridge's boot closure. This wrapper
|
|
153
|
+
* defers that to a lazy `await import()` reached ONLY on the owned-outcome resume
|
|
154
|
+
* branch (the decider awaits it). peers/self/list/mailbox-deliver therefore boot
|
|
155
|
+
* with no pi package present; a pi-less environment that DOES hit a spawn-bg resume
|
|
156
|
+
* surfaces an honest module-not-found at that point rather than failing boot.
|
|
157
|
+
*/
|
|
158
|
+
async function lazyProductionPreflight(input: PreflightInput): Promise<PreflightOutcome> {
|
|
159
|
+
const { preflight } = await import("./entwurf-preflight.ts");
|
|
160
|
+
return preflight(input);
|
|
161
|
+
}
|
|
162
|
+
|
|
141
163
|
/** Map a record-side socket inspection to the singleton (socketGids, symlinkedGids) the
|
|
142
164
|
* `isNonPiGardenIdSocketConflict` predicate consumes. `indeterminate` fails LOUD (QB2): an
|
|
143
165
|
* unprovable conflict must NOT be folded to "no conflict" — that would silently allow an
|
|
@@ -185,7 +207,7 @@ export function makeProductionEntwurfV2Deps(opts: ProductionEntwurfV2Opts): Entw
|
|
|
185
207
|
releaseLock: s.releaseLock ?? realReleaseLock,
|
|
186
208
|
inspectSocket: s.inspectSocket ?? inspectTargetControlSocket,
|
|
187
209
|
probeSocket: s.probeSocket ?? probeSocketLiveness,
|
|
188
|
-
preflight: s.preflight ??
|
|
210
|
+
preflight: s.preflight ?? lazyProductionPreflight,
|
|
189
211
|
classifyConnect: s.classifyConnect ?? classifyConnectError,
|
|
190
212
|
sendRpc: s.sendRpc ?? realSendRpc,
|
|
191
213
|
enqueue: s.enqueue ?? enqueueMetaMessage,
|
|
@@ -265,7 +287,7 @@ export function makeProductionEntwurfV2Deps(opts: ProductionEntwurfV2Opts): Entw
|
|
|
265
287
|
releaseLock: release,
|
|
266
288
|
inspectSocket,
|
|
267
289
|
probeSocket,
|
|
268
|
-
preflightForCwd: (cwd: string): PreflightOutcome =>
|
|
290
|
+
preflightForCwd: (cwd: string): PreflightOutcome | Promise<PreflightOutcome> =>
|
|
269
291
|
io.preflight({ cwd, agentDir: opts.agentDir, prefixRoots: opts.prefixRoots }),
|
|
270
292
|
mailboxDeliverabilityFor,
|
|
271
293
|
mailboxDir,
|
package/run.sh
CHANGED
|
@@ -13,7 +13,16 @@
|
|
|
13
13
|
#
|
|
14
14
|
set -euo pipefail
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
SOURCE="${BASH_SOURCE[0]}"
|
|
17
|
+
while [ -L "$SOURCE" ]; do
|
|
18
|
+
DIR="$(cd -P -- "$(dirname -- "$SOURCE")" && pwd)"
|
|
19
|
+
TARGET="$(readlink "$SOURCE")"
|
|
20
|
+
case "$TARGET" in
|
|
21
|
+
/*) SOURCE="$TARGET" ;;
|
|
22
|
+
*) SOURCE="$DIR/$TARGET" ;;
|
|
23
|
+
esac
|
|
24
|
+
done
|
|
25
|
+
REPO_DIR=$(cd -P -- "$(dirname -- "$SOURCE")" && pwd)
|
|
17
26
|
PROJECT_DIR_DEFAULT=$(pwd)
|
|
18
27
|
TARGET_PROJECT_DIR=${2:-$PROJECT_DIR_DEFAULT}
|
|
19
28
|
# npm publish identity. Scoped 2026-05-18 — bare `entwurf` was not on npm
|
|
@@ -36,6 +45,7 @@ Usage:
|
|
|
36
45
|
./run.sh release-gate [project-dir] [--allow-skip-gemini] # SINGLE release gate: full static (pnpm check) + the v2-native live gates (v2 matrix/spawn-resume-live, check-bridge, retargeted smoke-session-id-name, RGG) + the ACP plugin acceptance floor (11 LIVE smokes: socket-citizen/raw-turn/overlay/provider/session-reuse/carrier-augment/memory-containment/rgg/mcp/skill/bundled-mcp). TWO-TIER summary: MUST (release-blocking, owns the exit code — "green" applies here) + BEHAVIOR (advisory, non-blocking: RGG positives model-in-loop turn). LIVE-gated MUST steps HONEST-SKIP when LIVE!=1 (a CUT needs LIVE=1, SKIP=0). --allow-skip-gemini accepted-but-ignored (back-compat). final cut authorization is GLG's.
|
|
37
46
|
./run.sh check-bridge # entwurf-bridge direct MCP smoke + protocol/negative-path test.sh (live substrate = v2 live smokes)
|
|
38
47
|
./run.sh check-entwurf-bridge-boot # deterministic gate (5d-5-pre, G1a/G1b, IN pnpm check): boot start.sh under strip-types + assert v2 fence graph loads + entwurf_v2 registered/schema; tools/list only, no auth/side-effect
|
|
48
|
+
./run.sh check-entwurf-bridge-pi-free # deterministic gate (0.12.1 A, IN pnpm check): static — bridge index eager value-import closure must carry no @earendil-works/pi-* (type-only + dynamic import excluded); proves the meta-bridge boots pi-free
|
|
39
49
|
./run.sh check-model-lock # deterministic unit test for pi-extensions/model-lock.ts (4-quadrant + edge cases, no API)
|
|
40
50
|
./run.sh check-shell-quote # POSIX-safety gate for shellQuote (remote SSH arg quoting in entwurf paths) — source parity + behavior matrix, no SSH
|
|
41
51
|
./run.sh check-entwurf-session-identity # deterministic gate for locked garden session identity & name grammar (sessionId/buildSessionName/parse/collision), no API
|
|
@@ -83,6 +93,7 @@ Usage:
|
|
|
83
93
|
./run.sh smoke-meta-install-state # 1.0.0 meta-bridge Phase 2: stateful install/uninstall + store-doctor regression gate. Offline/deterministic (deps: bash+node+python3)
|
|
84
94
|
./run.sh smoke-meta-prune # 1.0.0 meta-bridge Phase 4: listing-only store janitor regression gate — classify keep/orphan/stale/ambiguous, delete nothing. Offline/deterministic (deps: bash+node)
|
|
85
95
|
./run.sh smoke-meta-keyset-guard # 0.10.0 meta-bridge: keyset-owner guard regression — check-keyset-overlap + managed-keys SSOT (disjoint passes, collisions fail). Offline/hermetic (deps: bash+python3)
|
|
96
|
+
./run.sh check-meta-manifest-schema # 0.12.2 meta-bridge: CLI-version-INDEPENDENT static guard — plugin manifests pinned to the minimal keyset that validates on the lowest supported Claude (closed-schema regression that broke 0.12.1 install on floor) + desired_mcp installed-vs-clone dual-mode. Offline (deps: python3)
|
|
86
97
|
./run.sh smoke-claude-native-resume-live # LIVE-only: Claude Code native fresh→--resume continuity + meta-record uniqueness; proves meta-bridge records identity without touching the backend resume path
|
|
87
98
|
|
|
88
99
|
./run.sh install-meta-bridge # 1.0.0 meta-bridge Phase 2: stateful GLOBAL install (plugin + USER MCP + settings keyset, honest uninstall state)
|
|
@@ -785,6 +796,17 @@ check_entwurf_bridge_boot() {
|
|
|
785
796
|
(cd "$REPO_DIR" && node --experimental-strip-types scripts/check-entwurf-bridge-boot.ts)
|
|
786
797
|
}
|
|
787
798
|
|
|
799
|
+
check_entwurf_bridge_pi_free() {
|
|
800
|
+
# 0.12.1 A-gate (static half): the entwurf-bridge MCP server must boot WITHOUT any
|
|
801
|
+
# pi package. entwurf is a harness-neutral npm package; pi is one optional adapter
|
|
802
|
+
# lane, not a boot dependency. Walks the EAGER static value-import closure of
|
|
803
|
+
# mcp/entwurf-bridge/src/index.ts and fails if any reachable module statically
|
|
804
|
+
# value-imports @earendil-works/pi-*. Type-only imports and dynamic `await import()`
|
|
805
|
+
# (the intended lazy preflight boundary) are excluded — the runtime boot smoke is the
|
|
806
|
+
# final authority that peers/self/list/mailbox-deliver come up pi-free.
|
|
807
|
+
(cd "$REPO_DIR" && node --experimental-strip-types scripts/check-entwurf-bridge-pi-free.ts)
|
|
808
|
+
}
|
|
809
|
+
|
|
788
810
|
check_entwurf_v2_production() {
|
|
789
811
|
# Deterministic gate for 0.11 Stage 0 step 5d-2b: makeProductionEntwurfV2Deps — the ctx-free
|
|
790
812
|
# PRODUCTION assembly of runEntwurfV2's deps. Proves the wiring over fake leaf-IO spies (no
|
|
@@ -1650,7 +1672,11 @@ check_pack() {
|
|
|
1650
1672
|
section "pack invariants (dry-run)"
|
|
1651
1673
|
|
|
1652
1674
|
local json
|
|
1653
|
-
|
|
1675
|
+
# --silent so the `prepack` build (pnpm --silent run build-bridge → tsc) and
|
|
1676
|
+
# npm's own lifecycle banner stay off stdout; otherwise they pollute the --json
|
|
1677
|
+
# payload this parses. prepack runs on dry-run too, which is how dist lands in
|
|
1678
|
+
# this gate's file list.
|
|
1679
|
+
json=$(cd "$REPO_DIR" && npm pack --dry-run --json --silent 2>/dev/null) || {
|
|
1654
1680
|
fail "[check-pack] npm pack --dry-run failed"
|
|
1655
1681
|
return 1
|
|
1656
1682
|
}
|
|
@@ -1701,6 +1727,10 @@ check_pack() {
|
|
|
1701
1727
|
"pi-extensions/entwurf-control.ts"
|
|
1702
1728
|
"pi-extensions/model-lock.ts" "pi-extensions/lib/entwurf-core.ts"
|
|
1703
1729
|
"mcp/entwurf-bridge/src/index.ts"
|
|
1730
|
+
# 0.12.1 C — the prepack-built node_modules-safe boot artifact. start.sh runs
|
|
1731
|
+
# this dist JS when present (the .ts source can't strip-types under
|
|
1732
|
+
# node_modules). prepack runs on `npm pack --dry-run`, so it is in this gate.
|
|
1733
|
+
"mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js"
|
|
1704
1734
|
"scripts/postinstall-chmod.cjs"
|
|
1705
1735
|
"pi/entwurf-capabilities.json"
|
|
1706
1736
|
"pi/entwurf-targets.json"
|
|
@@ -1796,6 +1826,15 @@ check_pack_install() {
|
|
|
1796
1826
|
tgz_path="${REPO_DIR}/${tgz_name}"
|
|
1797
1827
|
rm -f "$tgz_path"
|
|
1798
1828
|
|
|
1829
|
+
# 0.12.1 C — stale-dist guard. `tsc` emit does NOT prune orphaned files from
|
|
1830
|
+
# outDir, and `files: ["mcp/"]` would carry any leftover dist file into the
|
|
1831
|
+
# tarball. build-bridge therefore `rm -rf`s dist before emit. Prove it: plant a
|
|
1832
|
+
# sentinel in dist, then assert the pack's prepack (build-bridge) wiped it so it
|
|
1833
|
+
# never reaches the tarball. Without the clean step this sentinel ships.
|
|
1834
|
+
local stale_probe="${REPO_DIR}/mcp/entwurf-bridge/dist/__stale_probe__.js"
|
|
1835
|
+
mkdir -p "$(dirname "$stale_probe")"
|
|
1836
|
+
printf 'module.exports = "stale";\n' > "$stale_probe"
|
|
1837
|
+
|
|
1799
1838
|
echo "[check-pack-install] npm pack -> ${tgz_name}"
|
|
1800
1839
|
(cd "$REPO_DIR" && npm pack --dry-run=false 2>&1 | tail -1) || {
|
|
1801
1840
|
fail "[check-pack-install] npm pack failed"
|
|
@@ -1814,6 +1853,15 @@ check_pack_install() {
|
|
|
1814
1853
|
local tar_files pass=1 f pat
|
|
1815
1854
|
tar_files=$(tar -tf "$tgz_path" | sed 's|^package/||' | grep -v '/$' || true)
|
|
1816
1855
|
|
|
1856
|
+
# 0.12.1 C — the planted stale sentinel must NOT have survived into the tarball.
|
|
1857
|
+
# If it did, build-bridge's `rm -rf dist` clean step regressed and stale/orphan
|
|
1858
|
+
# emit can ship. (See the plant just before npm pack above.)
|
|
1859
|
+
if grep -qxF "mcp/entwurf-bridge/dist/__stale_probe__.js" <<<"$tar_files"; then
|
|
1860
|
+
rm -f "$tgz_path"
|
|
1861
|
+
fail "[check-pack-install] stale dist file shipped — build-bridge did not clean dist before emit (orphan-emit publish risk)"
|
|
1862
|
+
return 1
|
|
1863
|
+
fi
|
|
1864
|
+
|
|
1817
1865
|
# Required tarball contents. The old 0.11.0 ACP root files (index.ts,
|
|
1818
1866
|
# acp-bridge.ts, event-mapper.ts, engraving.ts, pi-context-augment.ts,
|
|
1819
1867
|
# pi-extensions/entwurf.ts) were removed on v2-only and are GONE — keeping them
|
|
@@ -1830,6 +1878,8 @@ check_pack_install() {
|
|
|
1830
1878
|
"pi-extensions/entwurf-control.ts"
|
|
1831
1879
|
"pi-extensions/model-lock.ts" "pi-extensions/lib/entwurf-core.ts"
|
|
1832
1880
|
"mcp/entwurf-bridge/src/index.ts"
|
|
1881
|
+
# 0.12.1 C — prepack-built node_modules-safe boot artifact (see check-pack).
|
|
1882
|
+
"mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js"
|
|
1833
1883
|
"scripts/postinstall-chmod.cjs"
|
|
1834
1884
|
"pi/entwurf-capabilities.json"
|
|
1835
1885
|
"pi/entwurf-targets.json"
|
|
@@ -1959,26 +2009,25 @@ check_pack_install() {
|
|
|
1959
2009
|
done
|
|
1960
2010
|
echo "[check-pack-install] pi loader smoke pass (entwurf registered, claude-sonnet-4-6 + claude-opus-4-8 anchor)"
|
|
1961
2011
|
|
|
1962
|
-
# npm-managed
|
|
1963
|
-
# `
|
|
1964
|
-
#
|
|
1965
|
-
#
|
|
1966
|
-
#
|
|
1967
|
-
#
|
|
1968
|
-
#
|
|
1969
|
-
#
|
|
1970
|
-
#
|
|
1971
|
-
# Prove `run.sh install` actually writes settings from the hoisted layout. HOME is
|
|
2012
|
+
# npm-managed neutral install regression — the README's PRIMARY install path is
|
|
2013
|
+
# now `npm install @junghanacs/entwurf` (NOT `pi install npm:...`). This layout
|
|
2014
|
+
# lands the package under node_modules, hoists runtime deps to the sibling
|
|
2015
|
+
# node_modules, has no package-local node_modules, and the pi peer trio must be
|
|
2016
|
+
# absent because pi is an optional adapter lane. The old cwd-relative
|
|
2017
|
+
# preflight_dep_integrity rejected every hoisted-dep npm install; 0.12.0 then
|
|
2018
|
+
# additionally died because start.sh tried strip-types under node_modules.
|
|
2019
|
+
# Prove `entwurf`/`entwurf-bridge` bins exist, `run.sh install` writes settings
|
|
2020
|
+
# from the hoisted layout, and the installed bridge boots from dist. HOME is
|
|
1972
2021
|
# redirected to a throwaway dir so ensure_agent_dir_symlinks operates on a temp
|
|
1973
|
-
# ~/.pi/agent and never touches
|
|
2022
|
+
# ~/.pi/agent and never touches the operator's real targets link.
|
|
1974
2023
|
if ! command -v npm >/dev/null 2>&1; then
|
|
1975
2024
|
fail "[check-pack-install] npm not on PATH — cannot run npm-managed install regression"
|
|
1976
2025
|
return 1
|
|
1977
2026
|
fi
|
|
1978
2027
|
local npmroot="$npm_tmp/npmroot" npmhome="$npm_tmp/npmhome" npmproj="$npm_tmp/npmproj" npm_log npm_pkg wire_log
|
|
1979
2028
|
mkdir -p "$npmroot" "$npmhome" "$npmproj"
|
|
1980
|
-
npm_log=$(cd "$npmroot" && npm install "$tgz_path" --
|
|
1981
|
-
fail "[check-pack-install] npm-managed install failed:"
|
|
2029
|
+
npm_log=$(cd "$npmroot" && npm install "$tgz_path" --no-audit --no-fund 2>&1) || {
|
|
2030
|
+
fail "[check-pack-install] npm-managed neutral install failed:"
|
|
1982
2031
|
echo "$npm_log" | tail -10 | sed 's/^/ /' >&2
|
|
1983
2032
|
return 1
|
|
1984
2033
|
}
|
|
@@ -1987,6 +2036,11 @@ check_pack_install() {
|
|
|
1987
2036
|
fail "[check-pack-install] npm-managed layout missing executable run.sh (postinstall-chmod did not run?) at $npm_pkg"
|
|
1988
2037
|
return 1
|
|
1989
2038
|
fi
|
|
2039
|
+
if [ ! -x "$npmroot/node_modules/.bin/entwurf" ] || [ ! -x "$npmroot/node_modules/.bin/entwurf-bridge" ]; then
|
|
2040
|
+
fail "[check-pack-install] npm-managed neutral install missing package bins (entwurf / entwurf-bridge)"
|
|
2041
|
+
ls -l "$npmroot/node_modules/.bin" 2>/dev/null | sed 's/^/ /' >&2 || true
|
|
2042
|
+
return 1
|
|
2043
|
+
fi
|
|
1990
2044
|
wire_log=$(HOME="$npmhome" "$npm_pkg/run.sh" install "$npmproj" 2>&1) || {
|
|
1991
2045
|
fail "[check-pack-install] npm-managed run.sh install failed (preflight rejected hoisted deps?):"
|
|
1992
2046
|
echo "$wire_log" | tail -15 | sed 's/^/ /' >&2
|
|
@@ -2004,6 +2058,75 @@ check_pack_install() {
|
|
|
2004
2058
|
fi
|
|
2005
2059
|
echo "[check-pack-install] npm-managed install regression pass (hoisted-dep run.sh install wrote settings)"
|
|
2006
2060
|
|
|
2061
|
+
# 0.12.1 C — installed bridge BOOT regression. This is the test whose absence
|
|
2062
|
+
# let the 0.12.0 install bug ship: the README's bridge launcher was
|
|
2063
|
+
# `node --experimental-strip-types src/index.ts`, which Node REFUSES under
|
|
2064
|
+
# node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING). Every gate above
|
|
2065
|
+
# wired settings or probed shape but never BOOTED the bridge from its installed
|
|
2066
|
+
# node_modules home, so the dead-on-arrival MCP server passed publish. Here we
|
|
2067
|
+
# boot the installed start.sh and assert it answers MCP tools/list with the v2
|
|
2068
|
+
# surface. Two proofs in one: (1) the prepack dist JS boots under node_modules
|
|
2069
|
+
# with plain node — a strip-types fallback would crash with the exact error
|
|
2070
|
+
# above, so a parseable tools/list IS proof the dist path was taken; (2) the
|
|
2071
|
+
# boot is pi-free — the @earendil-works peer trio is optional and must NOT be
|
|
2072
|
+
# installed by the neutral npm path, so the eager closure stands up with pi absent.
|
|
2073
|
+
local installed_start="$npmroot/node_modules/.bin/entwurf-bridge"
|
|
2074
|
+
local installed_dist="$npm_pkg/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js"
|
|
2075
|
+
if [ ! -f "$installed_dist" ]; then
|
|
2076
|
+
fail "[check-pack-install] installed bridge missing prebuilt dist (prepack did not emit into the tarball?): $installed_dist"
|
|
2077
|
+
return 1
|
|
2078
|
+
fi
|
|
2079
|
+
if [ -d "$npmroot/node_modules/@earendil-works" ]; then
|
|
2080
|
+
fail "[check-pack-install] @earendil-works present in npm-managed node_modules — pi-free boot proof is void (neutral npm install should not install optional pi peers)"
|
|
2081
|
+
return 1
|
|
2082
|
+
fi
|
|
2083
|
+
local boot_out
|
|
2084
|
+
if ! boot_out=$(START_SH="$installed_start" node --input-type=module <<'JS'
|
|
2085
|
+
import { spawn } from 'node:child_process';
|
|
2086
|
+
const start = process.env.START_SH;
|
|
2087
|
+
// Sanitize the child env so the pi-free proof cannot be masked by a leaked
|
|
2088
|
+
// module-resolution path: if NODE_PATH (or a stray pi env) pointed at a tree
|
|
2089
|
+
// holding @earendil-works, a statically pi-importing eager graph could resolve
|
|
2090
|
+
// and boot anyway, turning this gate falsely green. Strip it so "boots with
|
|
2091
|
+
// @earendil absent" stays an honest adversarial proof.
|
|
2092
|
+
const env = { ...process.env };
|
|
2093
|
+
delete env.NODE_PATH;
|
|
2094
|
+
const child = spawn(start, { stdio: ['pipe', 'pipe', 'pipe'], env });
|
|
2095
|
+
let stdout = '', stderr = '', done = false;
|
|
2096
|
+
const timer = setTimeout(() => {
|
|
2097
|
+
child.kill('SIGKILL');
|
|
2098
|
+
console.error('installed bridge boot timeout');
|
|
2099
|
+
if (stderr.trim()) console.error(stderr.trim());
|
|
2100
|
+
process.exit(1);
|
|
2101
|
+
}, 5000);
|
|
2102
|
+
function finish(trimmed) {
|
|
2103
|
+
if (done) return;
|
|
2104
|
+
done = true;
|
|
2105
|
+
clearTimeout(timer);
|
|
2106
|
+
let msg;
|
|
2107
|
+
try { msg = JSON.parse(trimmed); }
|
|
2108
|
+
catch { console.error('unparseable tools/list:', trimmed.slice(0, 300)); if (stderr.trim()) console.error(stderr.trim()); process.exit(1); }
|
|
2109
|
+
const names = (msg?.result?.tools ?? []).map((t) => t?.name).sort();
|
|
2110
|
+
for (const need of ['entwurf_v2', 'entwurf_peers', 'entwurf_self', 'entwurf_inbox_read']) {
|
|
2111
|
+
if (!names.includes(need)) { console.error('missing MCP tool from installed boot:', need, '— got', names.join(',')); process.exit(1); }
|
|
2112
|
+
}
|
|
2113
|
+
console.log(names.join(','));
|
|
2114
|
+
child.kill('SIGTERM');
|
|
2115
|
+
process.exit(0);
|
|
2116
|
+
}
|
|
2117
|
+
child.stdout.on('data', (d) => { stdout += d.toString(); const t = stdout.trim(); if (t) finish(t); });
|
|
2118
|
+
child.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
2119
|
+
child.on('error', (e) => { clearTimeout(timer); console.error('installed bridge spawn error:', String(e)); process.exit(1); });
|
|
2120
|
+
child.on('close', () => { if (done) return; clearTimeout(timer); if (stderr.trim()) console.error(stderr.trim()); console.error('installed bridge closed with empty tools/list'); process.exit(1); });
|
|
2121
|
+
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' }) + '\n');
|
|
2122
|
+
JS
|
|
2123
|
+
); then
|
|
2124
|
+
fail "[check-pack-install] installed bridge boot FAILED — the npm-installed MCP server does not answer tools/list (the 0.12.0 strip-types-under-node_modules regression):"
|
|
2125
|
+
echo "$boot_out" | tail -15 | sed 's/^/ /' >&2
|
|
2126
|
+
return 1
|
|
2127
|
+
fi
|
|
2128
|
+
echo "[check-pack-install] installed bridge boot pass (dist boots under node_modules, pi-free: $boot_out)"
|
|
2129
|
+
|
|
2007
2130
|
ok "[check-pack-install] publish install smoke pass"
|
|
2008
2131
|
return 0
|
|
2009
2132
|
}
|
|
@@ -2560,6 +2683,9 @@ case "$cmd" in
|
|
|
2560
2683
|
check-entwurf-bridge-boot)
|
|
2561
2684
|
check_entwurf_bridge_boot
|
|
2562
2685
|
;;
|
|
2686
|
+
check-entwurf-bridge-pi-free)
|
|
2687
|
+
check_entwurf_bridge_pi_free
|
|
2688
|
+
;;
|
|
2563
2689
|
check-entwurf-v2-spawn)
|
|
2564
2690
|
check_entwurf_v2_spawn
|
|
2565
2691
|
;;
|
|
@@ -2678,6 +2804,15 @@ case "$cmd" in
|
|
|
2678
2804
|
# Offline/hermetic (deps: bash+python3).
|
|
2679
2805
|
(cd "$REPO_DIR" && bash scripts/smoke-meta-keyset-guard.sh)
|
|
2680
2806
|
;;
|
|
2807
|
+
check-meta-manifest-schema)
|
|
2808
|
+
# 0.12.2 meta-bridge: deterministic, CLI-version-INDEPENDENT guard. `claude plugin
|
|
2809
|
+
# validate` is a CLOSED schema whose allowed keyset differs by version, so a
|
|
2810
|
+
# decorative key (0.12.1's root `description`) passed on the dev box but broke
|
|
2811
|
+
# install on the floor Claude. This pins the committed manifests to the minimal
|
|
2812
|
+
# validated keyset and asserts desired_mcp()'s installed-vs-clone dual-mode.
|
|
2813
|
+
# Offline/hermetic (deps: python3).
|
|
2814
|
+
(cd "$REPO_DIR" && python3 scripts/check-meta-manifest-schema.py)
|
|
2815
|
+
;;
|
|
2681
2816
|
smoke-meta-install-state)
|
|
2682
2817
|
# 1.0.0 meta-bridge Phase 2 regression gate: state file captures pre-install
|
|
2683
2818
|
# values, install/uninstall touches only the managed keyset, uninstall refuses
|