@junghanacs/entwurf 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +455 -0
- package/DELIVERY.md +24 -1
- package/README.md +2 -2
- package/VERIFY.md +19 -1
- package/demo/README.md +5 -2
- package/docs/acp-backend-rail.md +49 -2
- package/docs/setup-clean-host.md +4 -4
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +5 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/control-send-receipt.js +101 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +6 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +17 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +4 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +53 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +38 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +13 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-placement.js +11 -0
- package/mcp/entwurf-bridge/dist/protocol.js +0 -7
- package/mcp/entwurf-bridge/dist/scripts/doctor-pi-provider.js +40 -3
- package/mcp/entwurf-bridge/dist/scripts/herdr-rails.mjs +72 -0
- package/mcp/entwurf-bridge/src/index.ts +5 -4
- package/package.json +10 -9
- package/pi-extensions/entwurf-control.ts +27 -154
- package/pi-extensions/lib/compaction-send-guard.ts +5 -3
- package/pi-extensions/lib/control-send-receipt.ts +108 -0
- package/pi-extensions/lib/entwurf-v2-contract-schema.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-contract.ts +6 -2
- package/pi-extensions/lib/entwurf-v2-production.ts +19 -1
- package/pi-extensions/lib/entwurf-v2-runner.ts +23 -1
- package/pi-extensions/lib/entwurf-v2-send.ts +82 -6
- package/pi-extensions/lib/entwurf-v2-surface.ts +42 -3
- package/pi-extensions/lib/herdr-fresh-call.ts +13 -3
- package/pi-extensions/lib/herdr-placement.ts +11 -0
- package/protocol.js +0 -8
- package/run.sh +197 -29
- package/scripts/check-entwurf-v2-surface.ts +289 -10
- package/scripts/check-gate-qualification.ts +7 -4
- package/scripts/check-herdr-fresh-call.ts +10 -2
- package/scripts/check-herdr-placement.ts +51 -0
- package/scripts/check-herdr-plugin-build.ts +11 -4
- package/scripts/check-herdr-plugin.ts +745 -38
- package/scripts/check-mux-launcher-fence.ts +37 -0
- package/scripts/check-pi-launch.ts +7 -4
- package/scripts/check-release-gate-outcomes.ts +165 -0
- package/scripts/doctor-pi-provider.ts +44 -4
- package/scripts/herdr-rails.mjs +75 -0
- package/scripts/meta-bridge-state.py +18 -1
- package/scripts/mutants/bridge-command-boot.json +38 -0
- package/scripts/mutants/control-send-receipt.json +235 -0
- package/scripts/mutants/herdr-fresh-call.json +4 -4
- package/scripts/mutants/herdr-plugin-build.json +7 -11
- package/scripts/mutants/herdr-plugin.json +177 -9
- package/scripts/mutants/meta-retire.json +22 -0
- package/scripts/mutants/pack-install.json +4 -4
- package/scripts/mutants/pi-floor.json +17 -0
- package/scripts/mutants/release-gate.json +48 -0
- package/scripts/mutants/retired-sent-surface.json +32 -0
- package/scripts/mutants/v2-surface.json +7 -7
- package/scripts/smoke-codex-fresh-live.ts +94 -13
- package/scripts/smoke-herdr-fresh-call-live.ts +15 -8
- package/scripts/smoke-herdr-raw-install-live.sh +59 -2
- package/scripts/smoke-meta-install-state.sh +107 -1
- package/scripts/smoke-mux-lifecycle-live.ts +9 -2
- package/scripts/smoke-pi-provider-state.sh +106 -0
- package/scripts/smoke-setup-verdict.sh +18 -2
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* lets the hand's `decideReleasePolicy` fail loud on a mis-pairing.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
+
import type { AcceptanceBoundary } from "./control-send-receipt.ts";
|
|
31
32
|
import type {
|
|
32
33
|
DispatchDecision,
|
|
33
34
|
DispatchInput,
|
|
@@ -71,7 +72,15 @@ export interface DispatchExecutorDeps {
|
|
|
71
72
|
* and it is the ONLY enqueue-side datum carried (never a read timestamp; see
|
|
72
73
|
* `RpcSendResult`). */
|
|
73
74
|
export type ExecutedOutcome =
|
|
74
|
-
| {
|
|
75
|
+
| {
|
|
76
|
+
transport: "control-socket";
|
|
77
|
+
outcome: SendFinalOutcome;
|
|
78
|
+
rejectReason?: string;
|
|
79
|
+
messagePath?: string;
|
|
80
|
+
/** #120 P2: the receiver's acceptance boundary. The SURFACE renders this, not
|
|
81
|
+
* `outcome` — that one names the route/release leg, which an operator cannot act on. */
|
|
82
|
+
boundary?: AcceptanceBoundary;
|
|
83
|
+
}
|
|
75
84
|
| { transport: "meta-mailbox"; success: true; messagePath?: string }
|
|
76
85
|
// native-push carries `retried` so the surface can note the 1-shot re-probe retry fired.
|
|
77
86
|
| { transport: "native-push"; success: true; retried: boolean };
|
|
@@ -89,6 +98,15 @@ export type EntwurfV2RunResult =
|
|
|
89
98
|
receipt: SuccessReceipt;
|
|
90
99
|
transport: ExecutionPlan["transport"];
|
|
91
100
|
error: string;
|
|
101
|
+
/** #120 P2, N1: the ACCEPTANCE the send reached before the release failed. Without it
|
|
102
|
+
* the dirty-lock line is the one surface left that answers "what happened to my
|
|
103
|
+
* message" with an internal route word. Absent on a `rejected` finalization (nothing
|
|
104
|
+
* was accepted) and on the mailbox fallback leg (which carries a file instead). */
|
|
105
|
+
finalizedBoundary?: AcceptanceBoundary;
|
|
106
|
+
/** #98 R, same case: the `.msg` a mailbox fallback wrote before the release failed. */
|
|
107
|
+
finalizedMessagePath?: string;
|
|
108
|
+
/** The receiver's own refusal text, when the finalization was a `rejected` one. */
|
|
109
|
+
finalizedRejectReason?: string;
|
|
92
110
|
/** Present ONLY for the N1 case: the delivery/refusal reached a terminal outcome
|
|
93
111
|
* but `releaseLock` then threw (lock dirty). A re-send would double-deliver. */
|
|
94
112
|
finalizedOutcome?: Exclude<SendFinalOutcome, "failed">;
|
|
@@ -129,6 +147,7 @@ export async function executeDispatch(
|
|
|
129
147
|
outcome: r.outcome,
|
|
130
148
|
rejectReason: r.rejectReason,
|
|
131
149
|
messagePath: r.messagePath,
|
|
150
|
+
boundary: r.boundary,
|
|
132
151
|
},
|
|
133
152
|
};
|
|
134
153
|
} catch (err) {
|
|
@@ -140,6 +159,9 @@ export async function executeDispatch(
|
|
|
140
159
|
transport,
|
|
141
160
|
error: errorMessage(err),
|
|
142
161
|
finalizedOutcome: err.finalizedOutcome,
|
|
162
|
+
finalizedBoundary: err.finalizedBoundary,
|
|
163
|
+
finalizedMessagePath: err.finalizedMessagePath,
|
|
164
|
+
finalizedRejectReason: err.finalizedRejectReason,
|
|
143
165
|
releaseFailed: true,
|
|
144
166
|
retrySafe: false,
|
|
145
167
|
};
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
* `releaseLock` throw never MASKS a send failure — the original error wins (5b).
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
+
import type { AcceptanceBoundary } from "./control-send-receipt.ts";
|
|
35
36
|
import type { ExecutionPlan } from "./entwurf-v2-decider.ts";
|
|
36
37
|
import type { LockClaim } from "./entwurf-v2-lock.ts";
|
|
37
38
|
import {
|
|
@@ -50,6 +51,12 @@ export type MetaMailboxPlan = Extract<ExecutionPlan, { transport: "meta-mailbox"
|
|
|
50
51
|
* `send-final` outcomes. `fallback-sent` is a REAL final outcome (legacy parity: a
|
|
51
52
|
* re-resolved delivery is not a hidden retry). */
|
|
52
53
|
export type SendFinalOutcome = "sent" | "fallback-sent" | "rejected" | "failed";
|
|
54
|
+
// NOTE (#120 P2): the type above is the ROUTE/RELEASE algebra and is NOT the sender-visible
|
|
55
|
+
// delivery boundary. It answers WHICH LEG carried the bytes — first socket, re-resolved socket,
|
|
56
|
+
// mailbox — which is exactly what the release reducer needs and exactly what an operator cannot
|
|
57
|
+
// act on: `sent` here says a receiver accepted, never what the receiver then did with it. The
|
|
58
|
+
// boundary is a SEPARATE axis (`AcceptanceBoundary`, control-send-receipt.ts) riding beside this
|
|
59
|
+
// one, so neither can overwrite the other.
|
|
53
60
|
|
|
54
61
|
/** What a single RPC / mailbox enqueue reports. `success:false` is an in-band reject
|
|
55
62
|
* (the receiver answered and refused) — distinct from a thrown connect error.
|
|
@@ -66,6 +73,12 @@ export interface RpcSendResult {
|
|
|
66
73
|
success: boolean;
|
|
67
74
|
error?: string;
|
|
68
75
|
messagePath?: string;
|
|
76
|
+
/** #120 P2: the receiver's OWN acceptance boundary, already classified by
|
|
77
|
+
* `control-send-receipt.ts` at the wire seam. Present on every SUCCESSFUL control-socket
|
|
78
|
+
* answer — an answer we could not classify is `accepted-unknown-boundary`, not an absence —
|
|
79
|
+
* and absent on the mailbox rail, which has no turn-injection boundary to report and answers
|
|
80
|
+
* with a durable file receipt instead. */
|
|
81
|
+
boundary?: AcceptanceBoundary;
|
|
69
82
|
}
|
|
70
83
|
|
|
71
84
|
/** The same-lock one-shot re-resolve result (5c-2b implements the resolver; 5c-2a
|
|
@@ -116,6 +129,12 @@ export interface ControlSocketSendResult {
|
|
|
116
129
|
* mailbox delivery whose sender gets no per-message identifier — the same letter the
|
|
117
130
|
* primary mailbox rail names. */
|
|
118
131
|
messagePath?: string;
|
|
132
|
+
/** #120 P2: the acceptance boundary the receiver reported, on EVERY successful
|
|
133
|
+
* control-socket leg including the re-resolved one — a `fallback-sent` that dropped it would
|
|
134
|
+
* say "delivered" about a message sitting in a volatile queue behind whatever else is in it.
|
|
135
|
+
* Absent on the mailbox fallback leg (no turn-injection boundary exists there) and on
|
|
136
|
+
* rejected/failed. */
|
|
137
|
+
boundary?: AcceptanceBoundary;
|
|
119
138
|
}
|
|
120
139
|
|
|
121
140
|
// A drive step's verdict: the terminal outcome, plus the original error to RETHROW on
|
|
@@ -127,6 +146,24 @@ interface SendDrive {
|
|
|
127
146
|
rejectReason?: string;
|
|
128
147
|
/** #98 R receipt from the mailbox fallback leg (see `ControlSocketSendResult`). */
|
|
129
148
|
messagePath?: string;
|
|
149
|
+
/** #120 P2 acceptance boundary from a socket leg (see `ControlSocketSendResult`). */
|
|
150
|
+
boundary?: AcceptanceBoundary;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* A successful SOCKET answer always carries a boundary: the wire seam classifies every answer with
|
|
155
|
+
* one total function, and an answer it cannot place is `accepted-unknown-boundary` rather than
|
|
156
|
+
* nothing. So `undefined` here is not a host state, it is a bypassed seam — a dep wired straight
|
|
157
|
+
* past the classifier. Fail loud. A silent default would make the one shape that must never be
|
|
158
|
+
* mistaken for a direct trigger — delivered, boundary unknown to us — look exactly like one.
|
|
159
|
+
*/
|
|
160
|
+
function requireBoundary(result: RpcSendResult): AcceptanceBoundary {
|
|
161
|
+
if (result.boundary === undefined) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
"entwurf-v2-send: a successful control-socket RPC reported no acceptance boundary — the wire seam that classifies one was bypassed (contract violation).",
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return result.boundary;
|
|
130
167
|
}
|
|
131
168
|
|
|
132
169
|
/**
|
|
@@ -159,7 +196,12 @@ export async function executeControlSocketSend(
|
|
|
159
196
|
drive = { outcome: "failed", error: err };
|
|
160
197
|
}
|
|
161
198
|
finalizeRelease(policy, deps, held, drive);
|
|
162
|
-
return {
|
|
199
|
+
return {
|
|
200
|
+
outcome: drive.outcome,
|
|
201
|
+
rejectReason: drive.rejectReason,
|
|
202
|
+
messagePath: drive.messagePath,
|
|
203
|
+
boundary: drive.boundary,
|
|
204
|
+
};
|
|
163
205
|
}
|
|
164
206
|
|
|
165
207
|
/** Drive the 1차 send and route a connect failure through the F3 split. */
|
|
@@ -179,7 +221,7 @@ async function driveSend(plan: ControlSocketPlan, lock: LockClaim, deps: Control
|
|
|
179
221
|
}
|
|
180
222
|
// A completed RPC: ack ⇒ sent; in-band refusal ⇒ rejected, NO fallback. Preserve
|
|
181
223
|
// its receiver error verbatim when supplied; do not make a reason up when absent.
|
|
182
|
-
return result.success ? { outcome: "sent" } : inBandRejected(result);
|
|
224
|
+
return result.success ? { outcome: "sent", boundary: requireBoundary(result) } : inBandRejected(result);
|
|
183
225
|
}
|
|
184
226
|
|
|
185
227
|
/**
|
|
@@ -224,7 +266,7 @@ async function driveDeadFallback(
|
|
|
224
266
|
// connect failure finalizes as failed (no further fallback).
|
|
225
267
|
try {
|
|
226
268
|
const r = await deps.sendOverSocket(rePlan);
|
|
227
|
-
return r.success ? { outcome: "fallback-sent" } : inBandRejected(r);
|
|
269
|
+
return r.success ? { outcome: "fallback-sent", boundary: requireBoundary(r) } : inBandRejected(r);
|
|
228
270
|
} catch (err) {
|
|
229
271
|
return { outcome: "failed", error: err };
|
|
230
272
|
}
|
|
@@ -261,14 +303,42 @@ async function driveDeadFallback(
|
|
|
261
303
|
*/
|
|
262
304
|
export class SendDeliveredReleaseFailedError extends Error {
|
|
263
305
|
readonly finalizedOutcome: Exclude<SendFinalOutcome, "failed">;
|
|
306
|
+
/** #120 P2: the ACCEPTANCE the send actually reached, carried through the failure so the
|
|
307
|
+
* dirty-lock line can say what happened instead of a bare route word. Absent on a `rejected`
|
|
308
|
+
* finalization (nothing was accepted) and on the mailbox fallback leg (which carries a file
|
|
309
|
+
* instead) — those two are exactly why this is optional rather than defaulted. */
|
|
310
|
+
readonly finalizedBoundary?: AcceptanceBoundary;
|
|
311
|
+
/** #98 R receipt, same reason: a mailbox fallback that then failed to release still wrote a
|
|
312
|
+
* `.msg`, and the operator has to be told WHICH one before touching the lock. */
|
|
313
|
+
readonly finalizedMessagePath?: string;
|
|
314
|
+
/** The receiver's own refusal text on a `rejected` finalization. Without it the dirty-lock line
|
|
315
|
+
* is the ONE place a refusal loses its reason — and it is the place an operator reads while
|
|
316
|
+
* deciding by hand whether the message still needs sending. */
|
|
317
|
+
readonly finalizedRejectReason?: string;
|
|
264
318
|
readonly releaseError: unknown;
|
|
265
|
-
constructor(
|
|
319
|
+
constructor(
|
|
320
|
+
finalizedOutcome: Exclude<SendFinalOutcome, "failed">,
|
|
321
|
+
releaseError: unknown,
|
|
322
|
+
accepted: { boundary?: AcceptanceBoundary; messagePath?: string; rejectReason?: string } = {},
|
|
323
|
+
) {
|
|
266
324
|
const detail = releaseError instanceof Error ? releaseError.message : String(releaseError);
|
|
325
|
+
// NOT "delivered". The word was wrong on two of the three shapes that reach here — a
|
|
326
|
+
// `rejected` finalization delivered nothing at all, and a `queued-*` one is sitting in
|
|
327
|
+
// volatile memory — and DELIVERY.md refuses a bare `delivered` for exactly that reason.
|
|
328
|
+
// What is true of all three is that the send REACHED A TERMINAL RESULT and must not be
|
|
329
|
+
// repeated.
|
|
330
|
+
const what =
|
|
331
|
+
finalizedOutcome === "rejected"
|
|
332
|
+
? `rejected${accepted.rejectReason ? ` (${accepted.rejectReason})` : ""}`
|
|
333
|
+
: (accepted.boundary ?? "mailbox-enqueued");
|
|
267
334
|
super(
|
|
268
|
-
`entwurf-v2-send:
|
|
335
|
+
`entwurf-v2-send: the send finalized as \`${what}\` and releaseLock then failed (lock dirty, do NOT re-send): ${detail}`,
|
|
269
336
|
);
|
|
270
337
|
this.name = "SendDeliveredReleaseFailedError";
|
|
271
338
|
this.finalizedOutcome = finalizedOutcome;
|
|
339
|
+
this.finalizedBoundary = accepted.boundary;
|
|
340
|
+
this.finalizedMessagePath = accepted.messagePath;
|
|
341
|
+
this.finalizedRejectReason = accepted.rejectReason;
|
|
272
342
|
this.releaseError = releaseError;
|
|
273
343
|
}
|
|
274
344
|
}
|
|
@@ -294,7 +364,13 @@ function finalizeRelease(policy: ReleasePolicy, deps: ControlSocketSendDeps, loc
|
|
|
294
364
|
if (drive.outcome === "failed") throw original;
|
|
295
365
|
// N1: the delivery/refusal already happened — surface it as a structured,
|
|
296
366
|
// retry-unsafe error rather than a bare release throw.
|
|
297
|
-
|
|
367
|
+
// The acceptance truth rides along: without it the dirty-lock line is the ONE place
|
|
368
|
+
// left that still answers "what happened to my message" with a route word.
|
|
369
|
+
throw new SendDeliveredReleaseFailedError(drive.outcome, releaseErr, {
|
|
370
|
+
boundary: drive.boundary,
|
|
371
|
+
messagePath: drive.messagePath,
|
|
372
|
+
rejectReason: drive.rejectReason,
|
|
373
|
+
});
|
|
298
374
|
}
|
|
299
375
|
}
|
|
300
376
|
if (drive.outcome === "failed") throw original;
|
|
@@ -159,9 +159,35 @@ export function renderEntwurfV2Result(result: EntwurfV2RunResult): EntwurfV2Surf
|
|
|
159
159
|
// socket-to-socket retry (no file) and on every non-mailbox outcome, so the
|
|
160
160
|
// line degrades to the bare outcome rather than printing "undefined".
|
|
161
161
|
const enqueued = o.messagePath ? ` (enqueued ${basename(o.messagePath)})` : "";
|
|
162
|
+
if (delivered) {
|
|
163
|
+
// #120 P2 — THE BOUNDARY IS THE RECEIPT, and the route is an annotation.
|
|
164
|
+
// `sent` / `fallback-sent` name which LEG carried the bytes. An operator
|
|
165
|
+
// cannot act on that: for 78 minutes this line said `sent` about a message
|
|
166
|
+
// lying in a queue. So the visible word is what the RECEIVER observed —
|
|
167
|
+
// `sent` only for an idle direct trigger, `queued-steer` /
|
|
168
|
+
// `queued-follow-up` for the two volatile in-process queues, and
|
|
169
|
+
// `accepted-unknown-boundary` when a peer answered success in a shape this
|
|
170
|
+
// build cannot classify. That last one is never spelled with the word
|
|
171
|
+
// "sent": it was accepted, and saying "sent (unknown)" would put back the
|
|
172
|
+
// exact false claim this axis exists to remove.
|
|
173
|
+
//
|
|
174
|
+
// A re-resolved MAILBOX leg has no turn-injection boundary at all — it
|
|
175
|
+
// wrote a durable file — so it says `mailbox-enqueued` rather than a bare
|
|
176
|
+
// route word. The route survives as a separate annotation either way,
|
|
177
|
+
// because "your first socket was dead" is a real thing to have been told.
|
|
178
|
+
const route = o.outcome === "fallback-sent" ? " (via a re-resolved route)" : "";
|
|
179
|
+
// A re-resolved leg with no boundary IS the mailbox leg — that rail reports a
|
|
180
|
+
// durable file instead. A PRIMARY leg with no boundary is structurally
|
|
181
|
+
// impossible (the send hand throws on it), so if one ever arrives here it is
|
|
182
|
+
// named as the acceptance we could not classify rather than dressed as a
|
|
183
|
+
// mailbox file that was never written.
|
|
184
|
+
const boundary =
|
|
185
|
+
o.boundary ?? (o.outcome === "fallback-sent" ? "mailbox-enqueued" : "accepted-unknown-boundary");
|
|
186
|
+
return { text: `entwurf_v2 control-socket → ${boundary}${route}${enqueued}`, isError: false };
|
|
187
|
+
}
|
|
162
188
|
return {
|
|
163
189
|
text: `entwurf_v2 control-socket → ${o.outcome}${reason}${enqueued}`,
|
|
164
|
-
isError:
|
|
190
|
+
isError: true,
|
|
165
191
|
};
|
|
166
192
|
}
|
|
167
193
|
if (o.transport === "native-push") {
|
|
@@ -185,10 +211,23 @@ export function renderEntwurfV2Result(result: EntwurfV2RunResult): EntwurfV2Surf
|
|
|
185
211
|
}
|
|
186
212
|
case "execution-failed": {
|
|
187
213
|
if (result.releaseFailed && result.finalizedOutcome) {
|
|
188
|
-
// N1: the
|
|
214
|
+
// N1: the send reached a terminal result and `releaseLock` then threw.
|
|
215
|
+
//
|
|
216
|
+
// #120 P2 — THIS LINE USED TO SAY `DELIVERED (<route>)` ABOUT ALL THREE SHAPES THAT
|
|
217
|
+
// REACH IT, and it was wrong about two of them: a `rejected` finalization delivered
|
|
218
|
+
// nothing at all, and a `queued-*` one is sitting in volatile receiver memory. So
|
|
219
|
+
// the same acceptance vocabulary the ordinary receipt uses is named here too — a
|
|
220
|
+
// dirty lock is a reason to be MORE precise about what happened, not less, because
|
|
221
|
+
// the operator is about to decide by hand whether anything needs re-sending.
|
|
222
|
+
const what =
|
|
223
|
+
result.finalizedOutcome === "rejected"
|
|
224
|
+
? `rejected${result.finalizedRejectReason ? ` (${result.finalizedRejectReason})` : ""} — the receiver refused it; nothing was accepted`
|
|
225
|
+
: `${result.finalizedBoundary ?? "mailbox-enqueued"}${
|
|
226
|
+
result.finalizedMessagePath ? ` (enqueued ${basename(result.finalizedMessagePath)})` : ""
|
|
227
|
+
}`;
|
|
189
228
|
return {
|
|
190
229
|
text:
|
|
191
|
-
`entwurf_v2 ${result.transport}
|
|
230
|
+
`entwurf_v2 ${result.transport} finalized as ${what}, and the lock is DIRTY ` +
|
|
192
231
|
`(release failed) — do NOT retry, a re-send would double-deliver. Clear the lock by hand.` +
|
|
193
232
|
`\n error: ${result.error}`,
|
|
194
233
|
isError: true,
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
import { createHash } from "node:crypto";
|
|
45
45
|
import { statSync } from "node:fs";
|
|
46
46
|
import path from "node:path";
|
|
47
|
+
import { HERDR_FRESH_CALL_BACKENDS } from "../../scripts/herdr-rails.mjs";
|
|
47
48
|
import { callbackEnvAssignments } from "./callback-env.ts";
|
|
48
49
|
import {
|
|
49
50
|
buildOmpBootstrapPayload,
|
|
@@ -55,11 +56,20 @@ import {
|
|
|
55
56
|
TASK_MAX_CHARS,
|
|
56
57
|
} from "./fresh-call-composition.ts";
|
|
57
58
|
|
|
58
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* The pilot set, closed. `[#116 decision]` herdr's own `--kind` enum is much larger, and that is
|
|
59
61
|
* NOT evidence of support: every other backend is a pre-mutation named reject here, and there is
|
|
60
62
|
* no fallback to the tmux rail — a caller inside herdr who asks for codex gets a refusal, not a
|
|
61
|
-
* window somewhere else.
|
|
62
|
-
|
|
63
|
+
* window somewhere else.
|
|
64
|
+
*
|
|
65
|
+
* The VALUE now lives in `scripts/herdr-rails.mjs`, the one leaf the status pane and the install
|
|
66
|
+
* narration also read (#116 A7), so a fourth backend cannot appear in prose on one surface while
|
|
67
|
+
* this rail still refuses it. The re-export keeps this module's public name unchanged. The TYPE
|
|
68
|
+
* still has to be a tuple: a `.mjs` leaf infers `readonly string[]` without the JSDoc cast there,
|
|
69
|
+
* and `HerdrFreshCallBackend` would silently widen to `string` while every runtime check still
|
|
70
|
+
* passed. A gate pins that.
|
|
71
|
+
*/
|
|
72
|
+
export { HERDR_FRESH_CALL_BACKENDS };
|
|
63
73
|
export type HerdrFreshCallBackend = (typeof HERDR_FRESH_CALL_BACKENDS)[number];
|
|
64
74
|
|
|
65
75
|
/** Our backend name → the `--kind` token herdr accepts. `[측정 2026-09-14]` both round-tripped with
|
|
@@ -126,6 +126,17 @@ const OFFICIAL_REPORTS: Readonly<
|
|
|
126
126
|
* only diff in that file's `.jsonl` lines is a lambda parameter rename on the READER
|
|
127
127
|
* side. A source read alone would not have settled it, so the real file is the receipt.
|
|
128
128
|
*
|
|
129
|
+
* `[측정 2026-09-22, pi 0.87.0]` re-measured, and the shape of the evidence CHANGED for the better.
|
|
130
|
+
* Source: the three lines that BUILD a session name are byte-unchanged from 0.86.0
|
|
131
|
+
* (`packages/coding-agent/src/core/session-manager.ts` `${fileTimestamp}_${sessionId}.jsonl` at
|
|
132
|
+
* :1080/:1666/:1842, moved from :991/:1527/:1703 by the +148/−9 `SessionManager`-canonical work
|
|
133
|
+
* 0.87.0 declares as breaking). File: `scripts/check-herdr-placement.ts` now makes the installed
|
|
134
|
+
* `SessionManager` write a real session file into a temp dir on EVERY run — `create()` plus one
|
|
135
|
+
* `appendMessage`, zero model, zero network, zero child process — and asserts this parser reads
|
|
136
|
+
* that basename back to the id the file's own header declares. So the vendor floor below is no
|
|
137
|
+
* longer a receipt somebody took once; it is re-taken by the gate. The 0.86.0 hand-measured file
|
|
138
|
+
* stays a 0.86.0 fact in the bump ledger rather than being re-dated.
|
|
139
|
+
*
|
|
129
140
|
* WHY THIS IS PINNED HERE AND NOT INFERRED. The uuid is the join key and it lives in
|
|
130
141
|
* a VENDOR filename, so this rule depends on pi's naming and would break silently if
|
|
131
142
|
* pi changed it. **Vendor floor: pi 0.86.0, measured 2026-09-20** — whether that layout
|
package/protocol.js
CHANGED
|
@@ -21,11 +21,3 @@
|
|
|
21
21
|
* AGENTS.md section from its own pi-context augment.
|
|
22
22
|
*/
|
|
23
23
|
export const ENTWURF_PROJECT_CONTEXT_OPEN_TAG = "<project-context";
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Sender-side UI echo custom message type for successful entwurf_v2 send calls.
|
|
27
|
-
* The bridge emits it, the control renderer draws it, and the provider-level
|
|
28
|
-
* context filter removes it before LLM conversion. Keep as one SSOT: a typo
|
|
29
|
-
* here otherwise becomes a silent UI/context split.
|
|
30
|
-
*/
|
|
31
|
-
export const ENTWURF_SENT_MESSAGE_TYPE = "entwurf-sent";
|