@junghanacs/entwurf 0.12.9 → 0.12.10
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 +134 -248
- package/BASELINE.md +1 -1
- package/CHANGELOG.md +19 -0
- package/DELIVERY.md +4 -3
- package/README.md +26 -13
- package/VERIFY.md +3 -2
- package/demo/demo.sh +7 -1
- package/docs/setup-clean-host.md +10 -4
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +159 -66
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +5 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-core.js +26 -9
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-deliverability.js +10 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +15 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-facts.js +13 -13
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-resume-args.js +31 -41
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +8 -9
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +32 -25
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +4 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +5 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +9 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn-production.js +3 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +17 -20
- package/mcp/entwurf-bridge/src/index.ts +190 -82
- package/package.json +5 -3
- package/pi-extensions/entwurf-control.ts +69 -27
- package/pi-extensions/lib/acp/overlay.ts +5 -3
- package/pi-extensions/lib/entwurf-core.ts +26 -9
- package/pi-extensions/lib/entwurf-deliverability.ts +10 -6
- package/pi-extensions/lib/entwurf-fact-provider.ts +15 -17
- package/pi-extensions/lib/entwurf-facts.ts +13 -13
- package/pi-extensions/lib/entwurf-resume-args.ts +40 -46
- package/pi-extensions/lib/entwurf-self-address.ts +8 -9
- package/pi-extensions/lib/entwurf-v2-contract-schema.ts +7 -2
- package/pi-extensions/lib/entwurf-v2-contract.ts +32 -25
- package/pi-extensions/lib/entwurf-v2-decider.ts +8 -8
- package/pi-extensions/lib/entwurf-v2-lock.ts +5 -4
- package/pi-extensions/lib/entwurf-v2-production.ts +14 -11
- package/pi-extensions/lib/entwurf-v2-spawn-production.ts +3 -1
- package/pi-extensions/lib/meta-session.ts +17 -20
- package/run.sh +48 -41
- package/scripts/agy-bridge-config.py +249 -49
- package/scripts/agy-bridge.sh +59 -14
- package/scripts/check-acp-carrier-augment.ts +34 -2
- package/scripts/check-acp-sdk-surface.ts +22 -11
- package/scripts/check-agy-permission-matrix.py +655 -0
- package/scripts/check-entwurf-deliverability.ts +9 -6
- package/scripts/check-entwurf-fact-provider.ts +7 -7
- package/scripts/check-entwurf-facts.ts +12 -12
- package/scripts/check-entwurf-resume-args.ts +22 -60
- package/scripts/check-entwurf-self-address.ts +187 -4
- package/scripts/check-entwurf-v2-contract.ts +2 -2
- package/scripts/check-entwurf-v2-production.ts +5 -5
- package/scripts/check-entwurf-v2-spawn-production.ts +6 -2
- package/scripts/check-entwurf-v2-surface.ts +327 -14
- package/scripts/check-gate-qualification.ts +785 -0
- package/scripts/check-meta-identity-consumers.ts +1 -1
- package/scripts/check-shell-quote.ts +2 -1
- package/scripts/lib/mutation-qualify.ts +794 -0
- package/scripts/mutants/acp-augment.json +30 -0
- package/scripts/mutants/agy-permission.json +144 -0
- package/scripts/mutants/meta-identity.json +17 -0
- package/scripts/mutants/self-address.json +59 -0
- package/scripts/mutants/v2-surface.json +87 -0
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-acp-v2-send-live.ts +33 -18
- package/scripts/smoke-agy-install-state.sh +205 -20
- package/scripts/smoke-entwurf-v2-matrix-live.ts +3 -2
- package/pi-extensions/lib/entwurf-mailbox-guard.ts +0 -100
- package/scripts/check-entwurf-mailbox-guard.ts +0 -266
|
@@ -7,25 +7,28 @@
|
|
|
7
7
|
* `entwurf-v2-contract-schema.ts` (0.12.1 B-1) so this module — which the
|
|
8
8
|
* harness-neutral MCP bridge reaches at boot — carries no pi dependency.
|
|
9
9
|
*
|
|
10
|
-
* Why a frozen contract BEFORE the fact-provider (step 4):
|
|
11
|
-
*
|
|
12
|
-
* discovery first
|
|
13
|
-
*
|
|
14
|
-
* it; dispatch computes from facts at call
|
|
15
|
-
*
|
|
10
|
+
* Why a frozen contract BEFORE the fact-provider (step 4): the legacy 3-verb
|
|
11
|
+
* surface (`entwurf`/`entwurf_resume`/`entwurf_send`) was still live when this
|
|
12
|
+
* was written, and building discovery first would have baked verb-routing into
|
|
13
|
+
* the fact layer, taking `entwurf_peers` wrong (동결결정 10 순서 근거). So the
|
|
14
|
+
* SHAPE is locked here; the facts read it; dispatch computes from facts at call
|
|
15
|
+
* time (step 5). Those v1 verbs were REMOVED in the 0.12 cutover — the ordering
|
|
16
|
+
* argument is history, but the layering it produced is the live design.
|
|
16
17
|
*
|
|
17
18
|
* Source-verified invariants folded in (Opus 실측 + GPT 보정 + Fable R1-R5, 2026-06-11):
|
|
18
19
|
* - F1: caller intent is DECLARED in the input, so the contract a caller
|
|
19
20
|
* receives is deterministic — never computed from liveness at call time.
|
|
20
21
|
* `owned-outcome` (caller owns completion) ≠ `fire-and-forget` (ack only).
|
|
21
|
-
* - R1:
|
|
22
|
-
*
|
|
23
|
-
* so its liveness is `unsupported`, NOT folded into dead/indeterminate
|
|
24
|
-
*
|
|
25
|
-
*
|
|
22
|
+
* - R1: liveness is defined PER CAPABILITY DOMAIN. The control-socket domain
|
|
23
|
+
* currently contains backend `pi`; claude-code is self-fetch with no socket,
|
|
24
|
+
* so its socket liveness is `unsupported`, NOT folded into dead/indeterminate.
|
|
25
|
+
* This is transport capability, not identity rank: every target is first a
|
|
26
|
+
* record citizen. `unsupported` is a 4th FACT value, not a 4th table column.
|
|
26
27
|
* - R2: `target` is the garden-id of an EXISTING citizen. spawn-new is out of
|
|
27
|
-
* v2 scope
|
|
28
|
-
*
|
|
28
|
+
* v2 scope — it was the legacy `entwurf` verb's, and since that verb's removal
|
|
29
|
+
* fresh creation is a deferred capability with NO surface, not a fallback that
|
|
30
|
+
* still exists elsewhere. Absent/typo gid = `bad-target` (so F6 "오타 gid가
|
|
31
|
+
* 신규 spawn 사고 막기" holds automatically).
|
|
29
32
|
* - N1/F3: an `indeterminate` target never spawns. N2: `fire-and-forget` to a
|
|
30
33
|
* `dormant` target is "reject for now" (mailbox-wake lacks a reply-correlation
|
|
31
34
|
* id in the substrate; an additive extension later, not a permanent no).
|
|
@@ -67,16 +70,16 @@ export const DISPATCH_LIVENESSES = ["live", "dormant", "indeterminate"] as const
|
|
|
67
70
|
export type DispatchLiveness = (typeof DISPATCH_LIVENESSES)[number];
|
|
68
71
|
|
|
69
72
|
// ── Backend liveness domain (R1 + F4) ──────────────────────────────────────
|
|
70
|
-
// Backends whose SOCKET liveness predicate is DEFINED
|
|
71
|
-
// domain
|
|
73
|
+
// Backends whose SOCKET liveness predicate is DEFINED. The control-socket
|
|
74
|
+
// capability domain currently contains `pi` (connect + RPC `get_info`).
|
|
72
75
|
// claude-code (self-fetch, no socket) has no liveness predicate at all → `unsupported`.
|
|
73
76
|
// codex/antigravity are direct-inject; antigravity's liveness IS measured, but by the
|
|
74
77
|
// SEPARATE native-push adapter rail (a live app-server conversation probe), NOT this
|
|
75
78
|
// pi-socket domain — so it must NEVER be added here. Adding it would pull agy into the
|
|
76
|
-
//
|
|
77
|
-
// reporting agy `unsupported` = "outside the
|
|
79
|
+
// control-socket table (inspectSocket/probeSocket are socket-only); the fact layer keeps
|
|
80
|
+
// reporting agy `unsupported` = "outside the control-socket liveness domain", NOT
|
|
78
81
|
// unreachable (the native-push rail measures it — entwurf-v2-decider.ts). Widening
|
|
79
|
-
// THIS set is a deliberate future decision
|
|
82
|
+
// THIS set is a deliberate future decision, gated by a real compatible
|
|
80
83
|
// control-socket predicate — never by silently mapping sessions to dead/indeterminate
|
|
81
84
|
// (R1 핵심). check-entwurf-facts pins this == ["pi"] and asserts the native-push
|
|
82
85
|
// domain is disjoint from it.
|
|
@@ -89,12 +92,12 @@ export function isLivenessSupported(backend: string): boolean {
|
|
|
89
92
|
|
|
90
93
|
// ── Native-push backend domain (봉인 2/4) ───────────────────────────────────
|
|
91
94
|
// A backend whose liveness is measured by the SEPARATE native-push adapter rail (a
|
|
92
|
-
// live app-server conversation probe — antigravity's LS gRPC), NOT
|
|
93
|
-
// socket. This domain is DISJOINT from LIVENESS_DOMAIN_BACKENDS
|
|
94
|
-
//
|
|
95
|
+
// live app-server conversation probe — antigravity's LS gRPC), NOT a control
|
|
96
|
+
// socket. This domain is DISJOINT from LIVENESS_DOMAIN_BACKENDS: an agy session
|
|
97
|
+
// is `unsupported` on the socket FACT axis (entwurf_peers) yet fully
|
|
95
98
|
// measured + deliverable on the native-push axis. The two are separate rails on
|
|
96
99
|
// purpose — check-entwurf-facts pins both sets and asserts their intersection is ∅
|
|
97
|
-
// (a backend can never be in both
|
|
100
|
+
// (a backend can never be in both domains).
|
|
98
101
|
export const NATIVE_PUSH_BACKENDS = ["antigravity"] as const;
|
|
99
102
|
export type NativePushBackend = (typeof NATIVE_PUSH_BACKENDS)[number];
|
|
100
103
|
|
|
@@ -239,8 +242,9 @@ export const ENTWURF_V2_ACTIONS = ["send", "resume"] as const;
|
|
|
239
242
|
export const ENTWURF_V2_OWNERSHIPS = ["ack-only", "owned"] as const;
|
|
240
243
|
// Delivery mode of the message to the target (how it is injected) — steer =
|
|
241
244
|
// interrupt the current turn, follow_up = queue after it. A SEPARATE axis from
|
|
242
|
-
// both the intent/ownership axis (F1) and the liveness-routing axis
|
|
243
|
-
// entwurf_send
|
|
245
|
+
// both the intent/ownership axis (F1) and the liveness-routing axis. The removed
|
|
246
|
+
// v1 `entwurf_send` carried the same steer|follow_up surface, so this axis is
|
|
247
|
+
// inherited vocabulary, not a second live delivery verb.
|
|
244
248
|
export const ENTWURF_V2_MODES = ["steer", "follow_up"] as const;
|
|
245
249
|
|
|
246
250
|
export type DispatchVerdict =
|
|
@@ -252,7 +256,10 @@ export type DispatchVerdict =
|
|
|
252
256
|
// intent × dispatch-liveness → exactly one verdict (Q2). v2-initial ALLOWS
|
|
253
257
|
// exactly two cells (fire-and-forget+live = send; owned-outcome+dormant =
|
|
254
258
|
// resume); the other four reject. The reject cells are honest "지금은 없음"
|
|
255
|
-
// locks (N2)
|
|
259
|
+
// locks (N2). They were written while the legacy 3-verb surface still covered
|
|
260
|
+
// those flows; that surface is GONE, so a reject cell is now a real absence with
|
|
261
|
+
// no fallback verb behind it — reopening one takes a new contract, never a
|
|
262
|
+
// quiet re-admission.
|
|
256
263
|
export const DISPATCH_TABLE: Record<EntwurfIntent, Record<DispatchLiveness, DispatchVerdict>> = {
|
|
257
264
|
"fire-and-forget": {
|
|
258
265
|
live: { action: "send", transport: "control-socket", ownership: "ack-only" },
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* MCP-schema bypass for pi-native/internal callers).
|
|
27
27
|
* 2. resolveTarget — no citizen → bad-target; a record-less control socket →
|
|
28
28
|
* record-less-socket (#50 C4: the record is the sole address authority);
|
|
29
|
-
* quarantined (
|
|
29
|
+
* quarantined (out-of-socket-domain record sharing a socket/symlink) → target-address-conflict.
|
|
30
30
|
* PROBE-FREE.
|
|
31
31
|
* 3. backend → isLivenessSupported.
|
|
32
32
|
* 4. acquireLock — IN-DOMAIN ONLY (?7), BEFORE lstat/connect, so the probe
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
import type { MailboxDeliverabilityResult } from "./entwurf-deliverability.ts";
|
|
50
|
-
import {
|
|
50
|
+
import { isOutOfSocketDomainGardenIdConflict } from "./entwurf-facts.ts";
|
|
51
51
|
import type { PreflightOutcome } from "./entwurf-preflight.ts";
|
|
52
52
|
import {
|
|
53
53
|
type EntwurfIntent,
|
|
@@ -75,7 +75,7 @@ import type { SocketLiveness } from "./socket-probe.ts";
|
|
|
75
75
|
|
|
76
76
|
// Re-export the shared conflict predicate so producers of a TargetResolution have a
|
|
77
77
|
// single import site for it (it is the SAME fn the fact-provider listing uses).
|
|
78
|
-
export {
|
|
78
|
+
export { isOutOfSocketDomainGardenIdConflict };
|
|
79
79
|
|
|
80
80
|
// ── observe timeout (?3) ───────────────────────────────────────────────────
|
|
81
81
|
// The bounded wait 5c's release-watcher gives a spawned child to surface its
|
|
@@ -172,8 +172,8 @@ export type DispatchDecision =
|
|
|
172
172
|
|
|
173
173
|
// ── target resolution (E: single-target, not a whole-store scan) ────────────
|
|
174
174
|
// A non-null identity is an existing citizen. `preProbeAddressConflict` is the
|
|
175
|
-
// PROBE-FREE
|
|
176
|
-
//
|
|
175
|
+
// PROBE-FREE record-side conflict: a backend outside the control-socket domain
|
|
176
|
+
// sharing a real/symlinked socket (`isOutOfSocketDomainGardenIdConflict`). Production uses one
|
|
177
177
|
// readMetaIdentityByGardenId + a target socket/symlink check; the gate injects it.
|
|
178
178
|
// Do NOT call listEntwurfFacts here to find the target — its socket probe would run
|
|
179
179
|
// before the lock (the 1C TOCTOU). The shared predicate is the only thing the
|
|
@@ -377,12 +377,12 @@ export async function decideDispatch(input: DispatchInput, deps: DispatchDecider
|
|
|
377
377
|
return { kind: "execute", receipt, plan, lock: null };
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
-
// 4-5.
|
|
380
|
+
// 4-5. control-socket domain (currently backend pi): lock → inspect → route.
|
|
381
381
|
return decideInDomain(gardenId, input, deps, ctx, identity.cwd);
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
-
// ──
|
|
385
|
-
//
|
|
384
|
+
// ── control-socket-domain probe (steps 4-5; currently backend pi). Every target
|
|
385
|
+
// is record-backed; a record-less socket rejects before this branch. ────────────
|
|
386
386
|
// The lock lifecycle (B2) lives here: acquire BEFORE lstat/connect, every reject path
|
|
387
387
|
// releases explicitly (rejectAfterRelease), every execute path that keeps the lock sets
|
|
388
388
|
// retainLock=true, and a thrown IO error releases the still-held lock before rethrowing so
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* entwurf-v2-lock — the per-gid dispatch lock primitive (0.11 Stage 0 step 5a,
|
|
3
3
|
* 버킷 B F2). LOAD-BEARING: the guard against a double-spawn of the same dormant
|
|
4
4
|
* target by two V2 dispatchers that share the substrate through different entry
|
|
5
|
-
* points. SCOPE (honest): this protects v2/v2 only.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* points. SCOPE (honest): this protects v2/v2 only. It was written while the legacy
|
|
6
|
+
* `entwurf_resume` still ran unchanged (동결결정 10 scope A) without taking this
|
|
7
|
+
* lock, which left a v2/legacy concurrent-resume gap. That verb was REMOVED in the
|
|
8
|
+
* 0.12 cutover, so the gap is closed by subtraction — not by this lock growing to
|
|
9
|
+
* cover it. Any NEW resume entry point must take this lock or the gap reopens.
|
|
9
10
|
*
|
|
10
11
|
* ENVIRONMENT ASSUMPTION (stale reclaim): `hostname` equality is used as the
|
|
11
12
|
* proxy for "same machine", so a holder pid is reclaim-probed with kill(0) only
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
mailboxConversationalDeliverable,
|
|
43
43
|
receiverMarkerMatchesIdentity,
|
|
44
44
|
} from "./entwurf-deliverability.ts";
|
|
45
|
-
import {
|
|
45
|
+
import { isOutOfSocketDomainGardenIdConflict } from "./entwurf-facts.ts";
|
|
46
46
|
// 0.12.1 B-2: TYPE-ONLY import — `entwurf-preflight.ts` value-imports
|
|
47
47
|
// `@earendil-works/pi-coding-agent` (ProjectTrustStore), so a static value-import
|
|
48
48
|
// here would re-couple the harness-neutral MCP bridge to pi at boot
|
|
@@ -189,7 +189,7 @@ async function lazyProductionPreflight(input: PreflightInput): Promise<Preflight
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
/** Map a record-side socket inspection to the singleton (socketGids, symlinkedGids) the
|
|
192
|
-
* `
|
|
192
|
+
* `isOutOfSocketDomainGardenIdConflict` predicate consumes. `indeterminate` fails LOUD (QB2): an
|
|
193
193
|
* unprovable conflict must NOT be folded to "no conflict" — that would silently allow an
|
|
194
194
|
* unsupported-backend mailbox send onto a quarantined address. */
|
|
195
195
|
function conflictSetsFor(
|
|
@@ -294,20 +294,23 @@ export function makeProductionEntwurfV2Deps(opts: ProductionEntwurfV2Opts): Entw
|
|
|
294
294
|
};
|
|
295
295
|
}
|
|
296
296
|
const identity = io.readIdentity(gid, sessionsDir);
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
// and (b) turn a pi target's `indeterminate` lstat into a top-level throw, stealing it
|
|
302
|
-
// from the under-lock `inspectSocket → indeterminate → indeterminate-no-spawn` path.
|
|
297
|
+
// This pre-probe conflict applies only OUTSIDE the control-socket capability domain.
|
|
298
|
+
// An in-domain target's lstat/connect MUST run under the later per-target lock, so it
|
|
299
|
+
// short-circuits here with no pre-lock IO. Otherwise an indeterminate lstat would be
|
|
300
|
+
// stolen from the under-lock `inspectSocket → indeterminate-no-spawn` path.
|
|
303
301
|
if (isLivenessSupported(identity.backend)) {
|
|
304
302
|
return { identity, preProbeAddressConflict: false };
|
|
305
303
|
}
|
|
306
|
-
// Only
|
|
307
|
-
//
|
|
304
|
+
// Only a citizen outside the control-socket domain reaches this record-side lstat;
|
|
305
|
+
// `indeterminate` fails loud (QB2 — never silently "no conflict").
|
|
308
306
|
const inspection = await io.inspectPath(controlSocketPath(gid, controlSocketDir));
|
|
309
307
|
const { socketGids, symlinkedGids } = conflictSetsFor(gid, inspection);
|
|
310
|
-
const preProbeAddressConflict =
|
|
308
|
+
const preProbeAddressConflict = isOutOfSocketDomainGardenIdConflict(
|
|
309
|
+
identity.backend,
|
|
310
|
+
gid,
|
|
311
|
+
socketGids,
|
|
312
|
+
symlinkedGids,
|
|
313
|
+
);
|
|
311
314
|
return { identity, preProbeAddressConflict };
|
|
312
315
|
};
|
|
313
316
|
|
|
@@ -135,7 +135,9 @@ export function resolveResumeLaunchIdentity(plan: SpawnBgPlan): LaunchIdentity {
|
|
|
135
135
|
const record = readAddressableMetaIdentity(plan.sessionId);
|
|
136
136
|
if (record.backend !== "pi") {
|
|
137
137
|
throw new Error(
|
|
138
|
-
`entwurf-v2-spawn-production: ${plan.sessionId} is a ${record.backend} citizen —
|
|
138
|
+
`entwurf-v2-spawn-production: ${plan.sessionId} is a ${record.backend} citizen — ` +
|
|
139
|
+
`spawn-bg resume is a host-adapter capability, and its domain currently contains backend pi only. ` +
|
|
140
|
+
`This is a relaunch capability, not the control-socket rail and not citizen rank.`,
|
|
139
141
|
);
|
|
140
142
|
}
|
|
141
143
|
const sessionFile = record.transcriptPath;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* meta-session —
|
|
2
|
+
* meta-session — the shared V3 garden-citizen RECORD AUTHORITY.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* This module entered through the native meta-bridge (#30), then became the one
|
|
5
|
+
* identity store for every addressable citizen, including `backend:"pi"` (#50).
|
|
6
|
+
* A record is a backend-owned-session bib card: it binds the backend's native id
|
|
7
|
+
* and transcript pointer to a garden id without taking over that backend's runtime,
|
|
8
|
+
* auth, or transcript. The module name is history; the live identity schema is not
|
|
9
|
+
* a native-only or pi-exception axis.
|
|
10
10
|
*
|
|
11
11
|
* Two layers, clearly sectioned:
|
|
12
12
|
* 1. RECORD functions + types (mint / serialize / parse / certifyActiveStore /
|
|
@@ -61,7 +61,7 @@ import { generateSessionId, SESSION_ID_RE } from "./session-id.js";
|
|
|
61
61
|
// Errors
|
|
62
62
|
// ---------------------------------------------------------------------------
|
|
63
63
|
|
|
64
|
-
/** A
|
|
64
|
+
/** A garden-citizen record is malformed, or an input violates the record contract. */
|
|
65
65
|
export class MetaRecordError extends Error {
|
|
66
66
|
constructor(message: string) {
|
|
67
67
|
super(message);
|
|
@@ -142,8 +142,9 @@ export function requireNonEmptyString(value: unknown, field: string): string {
|
|
|
142
142
|
|
|
143
143
|
/** Validate the 3-backend NATIVE bridge axis (sender/receiver markers, capability
|
|
144
144
|
* drift guard). Not a record-schema validator: identity records take
|
|
145
|
-
* `requireCitizenBackend` (which admits `pi`)
|
|
146
|
-
*
|
|
145
|
+
* `requireCitizenBackend` (which admits `pi`). Markers stay native-3 because the
|
|
146
|
+
* pi adapter carries its record-established garden id into children via env rather
|
|
147
|
+
* than using the native-hook pid marker rail. */
|
|
147
148
|
export function requireBackend(value: unknown): MetaBackend {
|
|
148
149
|
if (typeof value !== "string" || !META_BACKENDS.includes(value as MetaBackend)) {
|
|
149
150
|
throw new MetaRecordError(
|
|
@@ -208,7 +209,7 @@ function isoNow(now: Date): string {
|
|
|
208
209
|
/** The one live identity schema number. */
|
|
209
210
|
export const META_SCHEMA_VERSION_V3 = 3 as const;
|
|
210
211
|
|
|
211
|
-
/**
|
|
212
|
+
/** Every backend admitted by the one V3 record-citizen schema. */
|
|
212
213
|
export const META_CITIZEN_BACKENDS = ["claude-code", "antigravity", "codex", "pi"] as const;
|
|
213
214
|
export type MetaCitizenBackend = (typeof META_CITIZEN_BACKENDS)[number];
|
|
214
215
|
|
|
@@ -510,15 +511,11 @@ export function parseMetaIdentity(json: string): MetaIdentity {
|
|
|
510
511
|
// const for the three existing backends (the drift guard) and COVERS exactly
|
|
511
512
|
// META_CITIZEN_BACKENDS (pi included).
|
|
512
513
|
//
|
|
513
|
-
//
|
|
514
|
-
//
|
|
515
|
-
//
|
|
516
|
-
//
|
|
517
|
-
//
|
|
518
|
-
// pi's dormant→resume→mailbox path is self-fetch-shaped, so pi is really
|
|
519
|
-
// BIMODAL; a single wakeMode field cannot express both. Splitting it
|
|
520
|
-
// (mailboxWakeMode vs controlSocketWakeMode) is out of 3C scope — for now the
|
|
521
|
-
// single field reports pi's primary live capability (direct-inject) honestly.
|
|
514
|
+
// `wakeMode` describes the last centimetre, not citizen rank. The control-socket
|
|
515
|
+
// adapter injects the body into a pi turn, so backend `pi` is `direct-inject`;
|
|
516
|
+
// Claude's mailbox is `self-fetch`; native-push adapters are direct injection.
|
|
517
|
+
// Dormant spawn-bg resume is a separate transport/ownership decision and does not
|
|
518
|
+
// turn the pi record into a mailbox citizen.
|
|
522
519
|
// ---------------------------------------------------------------------------
|
|
523
520
|
|
|
524
521
|
/** Bump only on a breaking capability-registry shape change; the parser refuses other versions. */
|
package/run.sh
CHANGED
|
@@ -94,7 +94,7 @@ Usage:
|
|
|
94
94
|
./run.sh check-meta-capability-source # deterministic gate (0.11 Stage 0 step 3D-3): capability-source cut-over — mint/parse read wakeMode/deliveryLevel from the registry (metaCapabilityFor, registry-driven via injection), not META_BACKEND_DESCRIPTORS; behaviour-preserving (registry ≡ const); the record.delivery slot 3D-3 preserved was deleted by 3D-4, no API
|
|
95
95
|
./run.sh check-socket-probe # deterministic gate (0.11 Stage 0, F3): three-valued control-socket liveness (alive|dead|indeterminate) — GC reclaims dead only, indeterminate survives; pure classify + 2-socket integration, no API
|
|
96
96
|
./run.sh check-project-trust-handler # deterministic gate (0.11 Stage 0, Trust 2층): project_trust handler — decideProjectTrust matrix (escape=inherited-false+interactive+trust-here→{yes,remember:true}; non-interactive→undecided; never undefined) + adapter single-writer, fake prompt, no UI
|
|
97
|
-
./run.sh check-entwurf-v2-contract # deterministic gate (0.11 Stage 0 step 4-pre, 동결결정 10 + Fable R1-R5): FROZEN entwurf_v2 contract — R1
|
|
97
|
+
./run.sh check-entwurf-v2-contract # deterministic gate (0.11 Stage 0 step 4-pre, 동결결정 10 + Fable R1-R5): FROZEN entwurf_v2 contract — R1 control-socket domain (currently pi; claude/codex/agy=unsupported, not folded), 6-cell intent×liveness table (single verdict, 2 allow/4 reject), N1 indeterminate-no-spawn, Q2 owned-live-no-autosend, R3 table↔receipt round-trip, R5 taxonomy, schema↔types drift; pure, no API
|
|
98
98
|
./run.sh check-entwurf-v2-lock # deterministic gate (0.11 Stage 0 step 5a, 버킷 B F2): per-gid dispatch LOCK primitive — openSync wx atomic acquire, second-acquire=target-locked conflict (holder JSON for human cleanup), nonce-owned release (successor survives late release), stale reclaim same-host+ESRCH-only (EPERM/remote/alive/unknown fail-closed), empty/corrupt=conflict not auto-deleted, F2-P1 malformed gid throws; real temp dir, deps injected
|
|
99
99
|
./run.sh check-entwurf-v2-decider # deterministic gate (0.11 Stage 0 step 5b): PURE dispatch decider decideDispatch — frozen 7-step order over injected fakes, lock acquire+release tracked so reject⇒no-plan-no-lock proven; pre-probe rejects observedLiveness=null, send/resume execute keep lock + mailbox no-lock (?7), resume plan no mode/provider/model, invalid gid throws (F2-P1); pure, no IO
|
|
100
100
|
./run.sh check-entwurf-v2-matrix # deterministic gate (0.11 Stage 0 step 5d-5 a): REACHABILITY + LOCK SSOT table — drives REAL decideDispatch over fakes, fixes every (target kind → transport → lock class) cell as one table (control-socket/meta-mailbox/spawn-bg + bad-target/conflict/locked/undeliverable/owned-live/dormant/indeterminate rejects), coverage pass fails on a dropped cell; thin coverage not a decider re-impl; pure, no IO
|
|
@@ -104,20 +104,19 @@ Usage:
|
|
|
104
104
|
./run.sh check-entwurf-v2-runner # deterministic gate (0.11 Stage 0 step 5d-1): execute-router (executeDispatch) routing an already-decided DispatchDecision to its 5c transport hand → one outcome-rich EntwurfV2RunResult. reject→rejected (no hand) / control/spawn/mailbox→matching hand with decision.lock verbatim / spawn lock-retained rides executed (fail-closed) / N3 rejectReason carried / N1 SendDeliveredReleaseFailedError→execution-failed{finalizedOutcome,releaseFailed,retrySafe:false}; fake hands, no IO
|
|
105
105
|
./run.sh check-entwurf-v2-mailbox # deterministic gate (0.11 Stage 0 step 5c-4, LAST 5c transport slice): ENQUEUE-ONLY meta-mailbox SEND body (executeMetaMailboxSend) + production sendViaMailbox adapter — sender→formatMetaMailboxBody with plan.wantsReply threaded (divergence from legacy hard false), sender absent→raw plan.message, enqueue opts EXACTLY {gardenId,body,sessionsDir,mailboxDir}, enqueue throw PROPAGATES (no success:false fold — mailbox has no in-band refuse); adapter NEVER touches lock (release is the hand's job); source guard: no release/routing seam
|
|
106
106
|
./run.sh check-entwurf-v2-spawn # deterministic gate (0.11 Stage 0 step 5c-3a): spawn-bg RESUME watcher hand (executeSpawnBgResume) wiring spawn + socket-observe IO onto the 5c-1 reducer — Fable-3: TIMEOUT IS NOT A RELEASE (bare observeTimeout→killChild, release 0; bounded killGrace then real socket-alive ∨ child-exited releases ×1); spawnChild throw→spawn-start-failed; no observation obtainable (grace elapses / post-spawn watch dep throws)→lock-retained fail-closed (released:false, evidence surfaced), NO direct-release hatch; IO-via-dep, controlled promises
|
|
107
|
-
./run.sh check-entwurf-resume-args # deterministic gate (0.11 Stage 0 step 5c-3b): resume-argv SSOT (buildResumePiArgs)
|
|
107
|
+
./run.sh check-entwurf-resume-args # deterministic gate (0.11 Stage 0 step 5c-3b): resume-argv SSOT (buildResumePiArgs) for the v2 spawn-bg RESIDENT citizen — the `legacy` one-shot variant and its async worker were removed 2026-07-27, so the shipped posture is the only one the gate drives: v2-control=--entwurf-control + no --no-extensions (keep-alive is the goal, resumed session stays addressable); keeps --mode json -p + prompt-as-turn (-p NOT dropped); explicitExtensionArgs preserved exactly once (#29); plan.launchArgs (--approve) ride before the prompt; null provider→no --provider
|
|
108
108
|
./run.sh check-entwurf-v2-spawn-production # deterministic gate (0.11 Stage 0 step 5c-3c): production SpawnBgResumeDeps factory (makeProductionSpawnBgResumeDeps) wiring the 5c-3a watcher's 6 IO seams — no real pi/socket/timer (that=opt-in smoke-entwurf-v2-spawn-live, OUT of pnpm check). socketWatchVerdict: address-conflict→forged (reject, never wait)/alive→alive/dead·indeterminate→wait; spawnChild builds v2-control argv (--entwurf-control, no --no-extensions, -p+prompt, --approve, cwd authority); awaitSocketAlive connectable→resolve / symlink→reject without connect / dead→wait→alive / abort-clears; awaitChildExit code + listener cleanup; awaitTimeout schedule + abort-clear; killChild=SIGTERM; proc-less child fails loud
|
|
109
109
|
./run.sh smoke-entwurf-v2-spawn-live # LIVE phase gate (0.11 Stage 0 step 5c-3c, D5) — OUT of pnpm check, needs LIVE=1. Exercises the production SpawnBgResumeDeps against REAL OS objects: S1 real unix socket → awaitSocketAlive resolves (real lstat+probe), symlink→forged, absent→abort settles; S2 real child → spawn-event resolve + SIGTERM kill + exit-code capture; S3 watcher integration → real timeout→kill→child-exited→release ×1. Does NOT spawn a real pi resume (that=5d matrix). Run before 5d: LIVE=1 ./run.sh smoke-entwurf-v2-spawn-live
|
|
110
110
|
./run.sh smoke-entwurf-v2-spawn-resume-live # 0.11.0 (A) ACCEPTANCE gate — OUT of pnpm check, needs LIVE=1. The FULL spawn-bg resident lifecycle: mint backend=pi identity → seed a REAL dormant pi session (one-shot into ~/.pi/agent/sessions) → runEntwurfV2(owned-outcome) routes dormant→spawn-bg resume → a REAL detached pi --entwurf-control child stands its socket up, resumes, DOES a model turn. Asserts executed/spawn-bg/socket-alive/released + lock released ×1 + no lock file + pid alive + socket connectable + resume USER & assistant OK nonces in the session JSONL. Model-in-loop IN. The gate v1 deprecation (0.12) is predicated on. Model: ENTWURF_LIVE_TARGET=<provider>/<model> (default openai-codex/gpt-5.4). LIVE=1 ./run.sh smoke-entwurf-v2-spawn-resume-live
|
|
111
111
|
./run.sh smoke-entwurf-v2-matrix-live # LIVE sentinel (0.11 Stage 0 step 5d-5, D4-b) — OUT of pnpm check, needs LIVE=1. Drives REAL production runEntwurfV2 deps over REAL OS objects, 4 cells: C1 control-socket (real pi --entwurf-control resident → RPC send → lock acquire→release ×1), C1b record-less socket (#50 C4: live record-less pi → EVERY intent rejected pre-probe record-less-socket, no lock, rendered hint names record authority + fresh-cut), C2 meta-mailbox deliverable (armed self-fetch citizen → real .msg enqueue, lock-free), C3 meta-mailbox guard (no armed receiver → reject, no garbage). Model-in-loop OUT (transport/lock/enqueue gate, GPT Q2); negative/timeout stay deterministic. Model: ENTWURF_LIVE_TARGET=<provider>/<model> (default openai-codex/gpt-5.4). LIVE=1 ./run.sh smoke-entwurf-v2-matrix-live
|
|
112
112
|
./run.sh smoke-agy-native-push-live # 봉인 8 LIVE acceptance for the native-push (agy) rail — OUT of pnpm check, needs LIVE=1 + AGY_CONVERSATION_ID (a live agy conversation). Drives the REAL antigravity adapter + register core + runEntwurfV2 (production deps): doctor-static preflight (dangling→FAIL, the ③ gate), probe route, register create/attach idempotency, fire→native-push delivered, post-send re-probe (D7 partial), owned-outcome→native-push-no-resume-authority, bogus-conv→native-push-probe-indeterminate. Meta-store isolated to a temp dir (only the agy round-trip is real; no real-store residue). LIVE=1 AGY_CONVERSATION_ID=<convId> ./run.sh smoke-agy-native-push-live
|
|
113
|
-
./run.sh check-entwurf-facts # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 1+2): PURE PeerFact core + resolveFactList union — R1 out-of-domain→unsupported, R3b
|
|
113
|
+
./run.sh check-entwurf-facts # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 1+2): PURE PeerFact core + resolveFactList union — R1 out-of-domain→unsupported, R3b socket-domain 4-value, facts-only keyset; union: PeerFact + RecordLessSocketFact by gardenId (#50 C4: record-less socket = diagnostic subject, gid+liveness only), dormant→dead, F3 indeterminate preserved, out-of-socket-domain+socket fail-loud; pure, no IO
|
|
114
114
|
./run.sh check-socket-discovery # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 3): SOCKET-axis scanSocketProbes — probes (dir sockets) ∪ (in-domain citizen canonical paths) 3-valued; dormant citizen no-file → dead (resumable, not unprobed), stall → indeterminate (F3), dir hygiene/dedup/missing-dir + e2e → resolveFactList; readdir/probe injected, no IO
|
|
115
115
|
./run.sh check-meta-listing # deterministic gate: META-STORE facts axis — kind-carrying entries; non-regular records are never read, parse/drift become diagnostics, duplicate nativeSessionId quarantines every rival but not unrelated citizens; strict throws / collect partial; pure injected IO
|
|
116
|
-
./run.sh check-entwurf-fact-provider # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 4b): ASSEMBLY listEntwurfFacts — listAllMetaIdentities→scanSocketProbes→pre-quarantine
|
|
116
|
+
./run.sh check-entwurf-fact-provider # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 4b): ASSEMBLY listEntwurfFacts — listAllMetaIdentities→scanSocketProbes→pre-quarantine out-of-socket-domain/socket conflicts→resolveFactList(clean)→{facts,diagnostics}; C-원칙: expected corruption (parse/collision)→diagnostics (listing survives), impossible invariant (dup/unprobed)→throw; collision quarantines BOTH PeerFact+socket; deps injected, no IO
|
|
117
117
|
./run.sh check-entwurf-peers-surface # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 4c): MCP entwurf_peers RENDER renderEntwurfPeers (#50 C4) — payload keyset exactly {peers, diagnostics}; FORBIDDEN keys sessions/socketOnly/controlDir/socketPath/count + no .sock in text (socket is transport, never identity); record-less socket = aggregated record-less-socket diagnostic (F8, liveness-keyed message, alive names fresh-cut); NO verb-routing key (JSON deep scan) NOR word (text), diagnostics both surfaces, empty→(none), unsupported shown; WIRING guard: both surfaces call provider+render, getLiveSessions + /entwurf-sessions gone; facts fabricated, no IO
|
|
118
|
-
./run.sh check-entwurf-self-address # deterministic gate (SE-1/SE-2 slice 1): self-addressability honesty predicate computeSelfAddressability — pi replyable ⟺ live socket; meta ⟺ recordBacked ∧ ownerAlive ∧ watchArmed (regression-proof record-present rows); SOURCE GUARD buildStrictPiSenderEnvelope drops hardcoded replyable:true + existsSync-probes socket, entwurf_self renders alive vs expected
|
|
118
|
+
./run.sh check-entwurf-self-address # deterministic gate (SE-1/SE-2 slice 1): self-addressability honesty predicate computeSelfAddressability — pi replyable ⟺ live socket; meta splits by RAIL: self-fetch ⟺ recordBacked ∧ ownerAlive ∧ watchArmed (regression-proof record-present rows), native-push ⟺ recordBacked ∧ probeAlive (separate axis — no mailbox fact may rescue or sink it), unsupplied rail fail-closed; SOURCE GUARD buildStrictPiSenderEnvelope drops hardcoded replyable:true + existsSync-probes socket, entwurf_self renders alive vs expected AND renders the meta rail per-rail (mailbox only inside the self-fetch branch; native-push denies an inbox and gates injection on the probe)
|
|
119
119
|
./run.sh check-entwurf-deliverability # deterministic gate (SE-1/SE-2 slice 2c): conversational-mailbox deliverability predicate — computeMetaReceiverActive (recordBacked ∧ ownerAlive ∧ watchArmed) + mailboxConversationalDeliverable (self-fetch AND active); direct-inject pi refused (SE-1), self-fetch dead/unarmed refused (SE-2); self-address shares the same atom
|
|
120
|
-
./run.sh check-entwurf-mailbox-guard # deterministic gate (SE-1/SE-2 slice 2d): guarded mailbox enqueue — PURE 0-call (undeliverable target leaves injected enqueue uncalled) + TMPDIR snapshot (refused send leaves mailbox byte-identical, accepted writes one .msg) + fact gathering from record/capability/receiver-marker
|
|
121
120
|
./run.sh check-native-push-adapter # deterministic gate (봉인 3/8): native-push adapter leaf (antigravity) via a FAKE runner — FULL pid scan (not head -1), dead vs indeterminate, VOLATILE route re-discovery (no cache), send argv+ANTIGRAVITY_LS_ADDRESS env, non-zero exit throws, NO adapter-level retry (executor-owned), resolveNativePushAdapter fail-fast
|
|
122
121
|
./run.sh check-native-push-register # deterministic gate (봉인 5): registerNativeConversation (entwurf_register_native core) via fake adapter + isolated mkdtemp store — live probe→CREATE, re-register→ATTACH (same gid, cwd refreshed, no dup), not-live probe→REFUSE (throws, no record), receiver-marker abstinence (보정① source guard)
|
|
123
122
|
./run.sh check-agy-sender-identity # deterministic gate (#46 sender lane): WHO is calling the bridge — real agy hook as a child process writes an antigravity sender marker keyed by its PARENT pid (never on upsert failure), and resolveTrustedMetaSenderIdentity over isolated stores yields 0→null / 1→identity on EITHER backend / two distinct live identities on one owner pid→THROW (never guess, never downgrade to anonymous). This is what turns an agy send from external-mcp/unknown-host into a replyable garden citizen
|
|
@@ -128,9 +127,11 @@ Usage:
|
|
|
128
127
|
./run.sh smoke-meta-honesty # 1.0.0 meta-bridge: honesty regression gate (#30 blockers) — doorbell counts ALL msgs honestly + hook logs failures as ERROR (best-effort, no scream). Offline/deterministic (deps: bash+node+python3)
|
|
129
128
|
./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)
|
|
130
129
|
./run.sh check-meta-doctor-oracle # 0.12.8 (#51): detection power of the release ORACLE — healthy fixture must reach `doctor: PASS`, then 21 planted defects (retired shell form, partial hand-patch, launcher bypass/repoint/provenance loss, malformed exec args, owner type drift, extra leaf/group, doorbell asyncRewake/path/timeout, no live bridge, stale receiver, ambiguous/missing cache, missing hook log/writer, failing CLI probes) must each turn it FAIL naming their own cause, plus a positive case pinning that a long-writing CLI is NOT a false negative. Offline/deterministic (deps: bash+node+python3)
|
|
131
|
-
./run.sh smoke-agy-install-state # agy MCP + exact permission ownership regression
|
|
132
|
-
./run.sh
|
|
133
|
-
./run.sh
|
|
130
|
+
./run.sh smoke-agy-install-state # agy MCP + exact permission ownership regression: isolated HOME+XDG, adopt/state/inverse, symlink refuse, setup degrade. Offline/deterministic
|
|
131
|
+
./run.sh check-agy-permission-matrix # AGY permission CONTRACT SPACE as a literal table (55 cells): parser-state × operation × settings × ownership × precedence with stated exclusion rules; expectations are hand-written literals, never read from the SUT. Offline/deterministic (deps: python3)
|
|
132
|
+
./run.sh check-gate-qualification # kill-proof qualification (the gate-of-gates): runner self-test (classifier truth table + synthetic negatives incl. wrong-reason/hang/control-red/impurity) + committed mutant manifests (scripts/mutants/*.json) run in an isolated snapshot repo under control→mutant→restore→control; the real checkout is never written. Evidence = claim IDs + killed mutant IDs, never assertion counts
|
|
133
|
+
./run.sh smoke-agy-statusline-state # agy ambient garden-id statusLine install/doctor/inverse regression. Offline/deterministic
|
|
134
|
+
./run.sh smoke-agy-hooks-state # agy PreInvocation birth/sender hook install/doctor/inverse + direct stdin→meta-record regression. Offline/deterministic
|
|
134
135
|
./run.sh smoke-user-scope-citizen # 0.12.6 install-boundary: pi packages[] registration SSOT (register-pi-package.py) — idempotent + preserves unrelated + normalizes stale + remove symmetry + fails loud. Offline/hermetic (deps: bash+python3)
|
|
135
136
|
./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)
|
|
136
137
|
./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)
|
|
@@ -745,7 +746,7 @@ check_entwurf_v2_contract() {
|
|
|
745
746
|
# contract (동결결정 10 + 버킷 B F1/F4/F6 + Fable R1-R5). The intent×liveness
|
|
746
747
|
# decision table is a constant; the "table cell ↔ dispatch receipt" round-trip
|
|
747
748
|
# is asserted exhaustively — THE executable proof F6 demands ("산문 금지").
|
|
748
|
-
# R1
|
|
749
|
+
# R1 control-socket capability domain (currently pi; claude-code/codex/antigravity =
|
|
749
750
|
# unsupported, never folded into dead/indeterminate). 6-cell table, single
|
|
750
751
|
# verdict per cell (Q2), 2 allow / 4 reject. N1 indeterminate never spawns;
|
|
751
752
|
# Q2 owned-outcome+live never auto-sends. R5 taxonomy covers table reasons +
|
|
@@ -948,14 +949,13 @@ check_entwurf_v2_spawn() {
|
|
|
948
949
|
|
|
949
950
|
check_entwurf_resume_args() {
|
|
950
951
|
# Deterministic gate for 0.11 Stage 0 step 5c-3b: the resume-argv SSOT
|
|
951
|
-
# (buildResumePiArgs)
|
|
952
|
-
#
|
|
953
|
-
# `--no-extensions`
|
|
954
|
-
#
|
|
955
|
-
#
|
|
956
|
-
#
|
|
957
|
-
#
|
|
958
|
-
# (--approve) before the prompt; null provider emits no --provider; no cross-contamination.
|
|
952
|
+
# (buildResumePiArgs). v2-control is the only shipped shape (the legacy one-shot variant and
|
|
953
|
+
# its launcher were removed 2026-07-27). Pins the resident posture: `--entwurf-control`
|
|
954
|
+
# present + `--no-extensions` never (the keep-alive is the goal — a resumed session stands
|
|
955
|
+
# its socket up and stays addressable), the headless `--mode json -p` prefix and the
|
|
956
|
+
# prompt-as-turn positional (`-p` NOT dropped), explicitExtensionArgs preserved exactly once
|
|
957
|
+
# (provider-resolution footgun #29), plan.launchArgs (--approve) before the prompt, and a
|
|
958
|
+
# null provider emitting no --provider.
|
|
959
959
|
run_ts scripts/check-entwurf-resume-args.ts
|
|
960
960
|
}
|
|
961
961
|
|
|
@@ -1266,7 +1266,7 @@ check_meta_listing() {
|
|
|
1266
1266
|
check_entwurf_fact_provider() {
|
|
1267
1267
|
# Deterministic gate for 0.11 Stage 0 step 4 (fact-provider slice 4b): the
|
|
1268
1268
|
# ASSEMBLY layer listEntwurfFacts. listAllMetaIdentities → scanSocketProbes →
|
|
1269
|
-
# pre-quarantine
|
|
1269
|
+
# pre-quarantine out-of-socket-domain/socket conflicts → resolveFactList(clean) →
|
|
1270
1270
|
# {facts, diagnostics}. Throw-vs-diagnostics policy (GPT힣 C-원칙): expected
|
|
1271
1271
|
# corruption (parse failure / gardenId↔socket collision) → diagnostics, listing
|
|
1272
1272
|
# survives; impossible wiring invariant (resolveFactList duplicate/unprobed) →
|
|
@@ -1297,7 +1297,8 @@ check_entwurf_self_address() {
|
|
|
1297
1297
|
# from env presence alone: a socketless pi session, or a meta citizen whose owner
|
|
1298
1298
|
# exited / whose idle-watch was never armed, all advertised replyable while
|
|
1299
1299
|
# delivery silently failed (SE-1). Asserts: PURE truth table (pi replyable ⟺
|
|
1300
|
-
# socketAlive; meta ⟺ recordBacked ∧ ownerAlive ∧
|
|
1300
|
+
# socketAlive; external never; meta BY RAIL — self-fetch ⟺ recordBacked ∧ ownerAlive ∧
|
|
1301
|
+
# watchArmed, native-push ⟺ recordBacked ∧ probeAlive, unsupplied rail fail-closed),
|
|
1301
1302
|
# incl. the two regression-proof rows (record-present + owner-dead / watch-unarmed)
|
|
1302
1303
|
# that stay meaningful after slice 3 mints records; SOURCE GUARD that
|
|
1303
1304
|
# buildStrictPiSenderEnvelope drops the hardcoded `replyable: true` and existsSync-
|
|
@@ -1319,17 +1320,6 @@ check_entwurf_deliverability() {
|
|
|
1319
1320
|
run_ts scripts/check-entwurf-deliverability.ts
|
|
1320
1321
|
}
|
|
1321
1322
|
|
|
1322
|
-
check_entwurf_mailbox_guard() {
|
|
1323
|
-
# Deterministic gate for the guarded mailbox enqueue (SE-1/SE-2 slice 2d) — the IO
|
|
1324
|
-
# orchestration conversational-reply sites use instead of enqueueMetaMessage directly.
|
|
1325
|
-
# Asserts (GPT Q5, both axes): PURE 0-call — an undeliverable target (dead receiver /
|
|
1326
|
-
# direct-inject pi / absent record) leaves the injected enqueue UNCALLED, a deliverable
|
|
1327
|
-
# one calls it exactly once; TMPDIR SNAPSHOT with the real enqueueMetaMessage — a refused
|
|
1328
|
-
# send leaves the mailbox tree byte-identical (file list + content hash, not just mtime),
|
|
1329
|
-
# an accepted send writes exactly one .msg; plus fact gathering from record/capability/marker.
|
|
1330
|
-
run_ts scripts/check-entwurf-mailbox-guard.ts
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
1323
|
check_native_push_adapter() {
|
|
1334
1324
|
# Deterministic gate for the native-push adapter LEAF (봉인 3/8). Drives
|
|
1335
1325
|
# createAntigravityAdapter with a FAKE runner (no real agy/ss/pgrep). Asserts: FULL pid
|
|
@@ -1695,7 +1685,7 @@ const SITES = [
|
|
|
1695
1685
|
['pi/meta-bridge/entwurf-meta-receive/scripts/hook-launch.sh', /Claude Code >= (\d+\.\d+\.\d+)/g, 1, 'launcher refusal message'],
|
|
1696
1686
|
['scripts/meta-bridge-claude-floor.sh', /THE FLOOR IS (\d+\.\d+\.\d+)/g, 1, 'floor helper rationale header'],
|
|
1697
1687
|
['docs/setup-clean-host.md', /`>=(\d+\.\d+\.\d+)`\*\* — the exec-form hook floor/g, 1, 'clean-host pin matrix'],
|
|
1698
|
-
['AGENTS.md', /Claude Code `>=(\d+\.\d+\.\d+)`/g, 1, 'AGENTS rule
|
|
1688
|
+
['AGENTS.md', /Claude Code `>=(\d+\.\d+\.\d+)`/g, 1, 'AGENTS rule 14'],
|
|
1699
1689
|
['README.md', /supported floor `>=(\d+\.\d+\.\d+)`/g, 1, 'README doctor description'],
|
|
1700
1690
|
['scripts/check-hook-launch-topology.ts', /\/(\d+)\\\.(\d+)\\\.(\d+)\//g, 1, 'topology gate floor assertion'],
|
|
1701
1691
|
];
|
|
@@ -2233,7 +2223,7 @@ check_acp_provider_surface() {
|
|
|
2233
2223
|
check_acp_sdk_surface() {
|
|
2234
2224
|
# Deterministic gate for the S2a ACP SDK dependency surface. Pins the three
|
|
2235
2225
|
# ACP runtime deps to the current oracle versions (@agentclientprotocol/sdk
|
|
2236
|
-
# 1.3.0 + claude-agent-acp 0.
|
|
2226
|
+
# 1.3.0 + claude-agent-acp 0.62.0 + @anthropic-ai/sdk 0.100.1), locks the
|
|
2237
2227
|
# peer-resolution that keeps claude-agent-sdk satisfiable (0.100.1, not the
|
|
2238
2228
|
# stale 0.91.1), asserts the wire SDK still value-exports the symbols the raw
|
|
2239
2229
|
# turn needs (silent-rename gate), and forbids any source-level anthropic SDK
|
|
@@ -3757,12 +3747,13 @@ wire_agy_bridge() {
|
|
|
3757
3747
|
# reported as an mcp_config problem and sends the operator to repair the wrong file.
|
|
3758
3748
|
case "$out" in
|
|
3759
3749
|
*"permission refused (symlink)"*|*"permission invalid JSON"*|*"permission could not be granted"*)
|
|
3760
|
-
# The MCP server IS registered — only the allow
|
|
3761
|
-
# Ask, so the bridge works but stops for a y/n on EVERY
|
|
3750
|
+
# The MCP server IS registered — only the allow rules failed. agy defaults every mcp action to
|
|
3751
|
+
# Ask, so the bridge works but stops for a y/n on EVERY entwurf tool call. Half-wired, and said
|
|
3762
3752
|
# so: the explicit installer fails loud on this; setup only degrades it, never hides it.
|
|
3763
|
-
echo "[setup] WARN: agy settings.json could not take our permission
|
|
3764
|
-
echo "[setup] agy will prompt on every
|
|
3765
|
-
echo "[setup]
|
|
3753
|
+
echo "[setup] WARN: agy settings.json could not take our permission rules — bridge REGISTERED but NOT GRANTED." >&2
|
|
3754
|
+
echo "[setup] agy will prompt on every entwurf tool call until our narrow rules" >&2
|
|
3755
|
+
echo "[setup] ($(python3 "$REPO_DIR/scripts/agy-bridge-config.py" permission-rules 2>/dev/null || echo 'mcp(entwurf-bridge/<tool>)'))" >&2
|
|
3756
|
+
echo "[setup] are in its permissions.allow (see the line above for why). setup continues." >&2
|
|
3766
3757
|
;;
|
|
3767
3758
|
*"refused (symlink)"*)
|
|
3768
3759
|
echo "[setup] WARN: agy mcp_config is a symlink — someone else's SSOT (transitional)." >&2
|
|
@@ -4356,9 +4347,6 @@ case "$cmd" in
|
|
|
4356
4347
|
check-entwurf-deliverability)
|
|
4357
4348
|
check_entwurf_deliverability
|
|
4358
4349
|
;;
|
|
4359
|
-
check-entwurf-mailbox-guard)
|
|
4360
|
-
check_entwurf_mailbox_guard
|
|
4361
|
-
;;
|
|
4362
4350
|
check-native-push-adapter)
|
|
4363
4351
|
check_native_push_adapter
|
|
4364
4352
|
;;
|
|
@@ -4445,6 +4433,25 @@ case "$cmd" in
|
|
|
4445
4433
|
# Offline + deterministic (deps: bash+python3).
|
|
4446
4434
|
(cd "$REPO_DIR" && bash scripts/smoke-agy-install-state.sh)
|
|
4447
4435
|
;;
|
|
4436
|
+
check-agy-permission-matrix)
|
|
4437
|
+
# The agy permission engine's CONTRACT SPACE as a literal table (55 cells + stated
|
|
4438
|
+
# exclusions R2/R4-R7): parser-state × operation × settings × ownership × precedence.
|
|
4439
|
+
# Every recent permission defect was an unenumerated cell of this matrix; this gate
|
|
4440
|
+
# states the axes and exhausts the meaningful product, with hand-written literal
|
|
4441
|
+
# expectations (oracle independence — nothing is read back from the SUT). Sandbox
|
|
4442
|
+
# per cell; offline + deterministic (deps: python3).
|
|
4443
|
+
(cd "$REPO_DIR" && python3 scripts/check-agy-permission-matrix.py)
|
|
4444
|
+
;;
|
|
4445
|
+
check-gate-qualification)
|
|
4446
|
+
# Kill-proof qualification — the gate-of-gates. Proves the committed defect mutants
|
|
4447
|
+
# (scripts/mutants/*.json) make their gates fail BOUNDED and FOR THE CLAIMED REASON
|
|
4448
|
+
# ([QK:<claim>] token on a failure line), inside an isolated snapshot repo under the
|
|
4449
|
+
# control→mutant→restore→control state machine; the real checkout is never written
|
|
4450
|
+
# (HEAD + work-surface content hash asserted identical before/after). The runner is negative-controlled
|
|
4451
|
+
# first: zero-match/multi-match/survived/wrong-reason/hang(+pgroup grandchild kill)/
|
|
4452
|
+
# control-red/impurity all must classify red before any real manifest counts.
|
|
4453
|
+
run_ts scripts/check-gate-qualification.ts
|
|
4454
|
+
;;
|
|
4448
4455
|
smoke-agy-statusline-state)
|
|
4449
4456
|
# #46 Task 1 regression gate for the agy statusLine install adapter: install→doctor→uninstall
|
|
4450
4457
|
# in an ISOLATED HOME+XDG with a fake stable bin (entwurf-agy-statusline) + fake pgrep —
|