@akagilnc/pi-workflow-roles 0.1.4489 → 0.1.4508
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/dist/acp-host/production-host.js +372 -181
- package/dist/headless-host/production-host.js +372 -181
- package/dist/public-cli/countersign-run.js +214 -155
- package/dist/public-cli/diarist-run.js +4 -1
- package/dist/public-cli/invocation.js +129 -21
- package/dist/public-cli/main.js +372 -181
- package/package.json +1 -1
- package/src/public-cli/countersign-run.ts +326 -218
- package/src/public-cli/diarist-run.ts +4 -0
- package/src/public-cli/invocation.ts +205 -23
|
@@ -13,27 +13,34 @@
|
|
|
13
13
|
* effect is what this seat currently does. 起居录 path delivery is owned once by
|
|
14
14
|
* post-admission (#709 / ADR 0081).
|
|
15
15
|
*
|
|
16
|
-
* Wiring (#771):
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Wiring (#771 / #863): resolve typed identity before materializing the admitted
|
|
17
|
+
* run; same-ticket resume writes only to the retained run. 起居郎 escalate (认不出)
|
|
18
|
+
* and typed failure terminals
|
|
19
19
|
* (incl. verification failure) settle as countersign controlled failure — never
|
|
20
20
|
* wash into 真无票. Only a true missing lawful typed terminal stays unbound and
|
|
21
21
|
* continues the body (r5 unbound-continue). Bound refresh hands the typed key to
|
|
22
22
|
* 起居郎 so freeze loads issue face (ADR 0075: 每次过庭都跑是调用者用法 / typed handoff).
|
|
23
23
|
*/
|
|
24
|
-
import type {
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
import type {
|
|
25
|
+
DurablePrincipalAuthority,
|
|
26
|
+
RoleTurnRequest,
|
|
27
|
+
} from "../host-contracts.ts";
|
|
27
28
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
engineSessionMaterialFromOptions,
|
|
30
|
+
pickEngineAxis,
|
|
31
|
+
} from "../package-resources/engine-material.ts";
|
|
32
|
+
import { CliUsageError } from "./cli-errors.ts";
|
|
33
|
+
import { projectCourtTicketNumbers } from "../diarist-contracts.ts";
|
|
30
34
|
import { isSafePositiveTicketNumber } from "../run-ticket-number.ts";
|
|
31
35
|
import {
|
|
32
36
|
admitCountersignInvocation,
|
|
33
37
|
bindAdmittedTicketNumber,
|
|
34
38
|
bindCourtTicketNumbersOnAdmitted,
|
|
35
39
|
buildCountersignTransportPrompt,
|
|
40
|
+
freezePreparedAttachmentsIntoRun,
|
|
41
|
+
materializeCountersignInvocation,
|
|
36
42
|
relocateAdmittedRunToTicket,
|
|
43
|
+
withPreparedAttachments,
|
|
37
44
|
type AdmittedCountersignInvocation,
|
|
38
45
|
type ParseCountersignArgvResult,
|
|
39
46
|
} from "./invocation.ts";
|
|
@@ -49,7 +56,6 @@ import {
|
|
|
49
56
|
import {
|
|
50
57
|
loadResumableCountersignRun,
|
|
51
58
|
markRunAdmitted,
|
|
52
|
-
markRunTerminal,
|
|
53
59
|
type PublicResumeRequest,
|
|
54
60
|
type RunWriterLease,
|
|
55
61
|
type SameTicketSummonsMaterials,
|
|
@@ -155,11 +161,15 @@ function courtTicketNumbersFromOutcome(
|
|
|
155
161
|
roleOutcome.kind === "accepted" ||
|
|
156
162
|
roleOutcome.kind === "audit_escalation" ||
|
|
157
163
|
roleOutcome.kind === "failure"
|
|
158
|
-
? roleOutcome.payloads ?? []
|
|
164
|
+
? (roleOutcome.payloads ?? [])
|
|
159
165
|
: [];
|
|
160
166
|
let latest: readonly number[] | undefined;
|
|
161
167
|
for (const payload of payloads) {
|
|
162
|
-
if (
|
|
168
|
+
if (
|
|
169
|
+
typeof payload !== "object" ||
|
|
170
|
+
payload === null ||
|
|
171
|
+
Array.isArray(payload)
|
|
172
|
+
) {
|
|
163
173
|
continue;
|
|
164
174
|
}
|
|
165
175
|
const record = payload as Record<string, unknown>;
|
|
@@ -173,25 +183,34 @@ function courtTicketNumbersFromOutcome(
|
|
|
173
183
|
if (members.length === 0) {
|
|
174
184
|
// Literal [] is the intentional principal-only replace; non-empty garbage is not.
|
|
175
185
|
if (raw.length === 0) {
|
|
176
|
-
latest =
|
|
186
|
+
latest =
|
|
187
|
+
projectCourtTicketNumbers([], { principalTicket }) ?? undefined;
|
|
177
188
|
}
|
|
178
189
|
continue;
|
|
179
190
|
}
|
|
180
|
-
latest =
|
|
181
|
-
projectCourtTicketNumbers(raw, { principalTicket }) ?? undefined;
|
|
191
|
+
latest = projectCourtTicketNumbers(raw, { principalTicket }) ?? undefined;
|
|
182
192
|
}
|
|
183
193
|
return latest;
|
|
184
194
|
}
|
|
185
195
|
|
|
186
196
|
/** Routing boolean over the child's own typed sequence — does not pick or rewrite a sole row. */
|
|
187
|
-
function courtDiaristEscalated(
|
|
197
|
+
function courtDiaristEscalated(
|
|
198
|
+
roleOutcome: TerminalRoleOutcome | undefined,
|
|
199
|
+
): boolean {
|
|
188
200
|
if (roleOutcome === undefined) return false;
|
|
189
201
|
if (roleOutcome.kind === "audit_escalation") return true;
|
|
190
202
|
if (roleOutcome.kind !== "accepted") return false;
|
|
191
203
|
return (roleOutcome.payloads ?? []).some((payload) => {
|
|
192
|
-
if (
|
|
204
|
+
if (
|
|
205
|
+
typeof payload !== "object" ||
|
|
206
|
+
payload === null ||
|
|
207
|
+
Array.isArray(payload)
|
|
208
|
+
)
|
|
209
|
+
return false;
|
|
193
210
|
const record = payload as Record<string, unknown>;
|
|
194
|
-
return
|
|
211
|
+
return (
|
|
212
|
+
record.status === "escalate" || record.countersignStatus === "escalate"
|
|
213
|
+
);
|
|
195
214
|
});
|
|
196
215
|
}
|
|
197
216
|
|
|
@@ -203,16 +222,20 @@ function courtDiaristEscalated(roleOutcome: TerminalRoleOutcome | undefined): bo
|
|
|
203
222
|
* When `boundTicketNumber` is set (typed handoff from countersign), diarist
|
|
204
223
|
* binds under that key before the turn — never mechanical recognition from prose.
|
|
205
224
|
*/
|
|
206
|
-
export async function invokeCourtDiarist(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
225
|
+
export async function invokeCourtDiarist(
|
|
226
|
+
input: {
|
|
227
|
+
readonly instruction: string;
|
|
228
|
+
readonly projectRoot: string;
|
|
229
|
+
readonly failureLabel: string;
|
|
230
|
+
readonly attachmentPaths?: readonly string[];
|
|
231
|
+
/** Direct parent run id for durable child lineage (ADR 0010). */
|
|
232
|
+
readonly correlationId?: string;
|
|
233
|
+
/** Already-verified typed key from countersign (refresh / post-assert handoff). */
|
|
234
|
+
readonly boundTicketNumber?: number;
|
|
235
|
+
},
|
|
236
|
+
env: CourtDiaristSummonEnv,
|
|
237
|
+
io: CliIo,
|
|
238
|
+
): Promise<CourtDiaristInvocationResult> {
|
|
216
239
|
// Quiet face: the countersign caller must not see diarist CLI chatter.
|
|
217
240
|
const quietIo: CliIo = {
|
|
218
241
|
stdout() {},
|
|
@@ -239,13 +262,17 @@ export async function invokeCourtDiarist(input: {
|
|
|
239
262
|
io: quietIo,
|
|
240
263
|
...(env.credentials === undefined ? {} : { credentials: env.credentials }),
|
|
241
264
|
...(env.signal === undefined ? {} : { signal: env.signal }),
|
|
242
|
-
...(input.correlationId === undefined
|
|
265
|
+
...(input.correlationId === undefined
|
|
266
|
+
? {}
|
|
267
|
+
: { correlationId: input.correlationId }),
|
|
243
268
|
...(input.boundTicketNumber === undefined
|
|
244
269
|
? {}
|
|
245
270
|
: { boundTicketNumber: input.boundTicketNumber }),
|
|
246
271
|
// Child seat selects from the composition-root table. Do not pass the
|
|
247
272
|
// already-selected parent adapter (#840 / ADR 0082: --host 旗标>席位配置>缺省 pi).
|
|
248
|
-
...(env.hostAdapters === undefined
|
|
273
|
+
...(env.hostAdapters === undefined
|
|
274
|
+
? {}
|
|
275
|
+
: { hostAdapters: env.hostAdapters }),
|
|
249
276
|
});
|
|
250
277
|
|
|
251
278
|
const roleOutcome = result.terminal?.roleOutcome;
|
|
@@ -270,9 +297,13 @@ export async function invokeCourtDiarist(input: {
|
|
|
270
297
|
};
|
|
271
298
|
}
|
|
272
299
|
|
|
273
|
-
const asserted = (result.admitted as { ticketNumber?: number } | undefined)
|
|
300
|
+
const asserted = (result.admitted as { ticketNumber?: number } | undefined)
|
|
301
|
+
?.ticketNumber;
|
|
274
302
|
if (isSafePositiveTicketNumber(asserted)) {
|
|
275
|
-
const courtTicketNumbers = courtTicketNumbersFromOutcome(
|
|
303
|
+
const courtTicketNumbers = courtTicketNumbersFromOutcome(
|
|
304
|
+
roleOutcome,
|
|
305
|
+
asserted,
|
|
306
|
+
);
|
|
276
307
|
return {
|
|
277
308
|
identity: {
|
|
278
309
|
kind: "ticket",
|
|
@@ -348,7 +379,9 @@ export async function runCountersignCourtDiaristStation(
|
|
|
348
379
|
);
|
|
349
380
|
}
|
|
350
381
|
if (outcome.failedWithoutEscalate !== undefined) {
|
|
351
|
-
throw new StationChildExhaustedError(
|
|
382
|
+
throw new StationChildExhaustedError(
|
|
383
|
+
outcome.failedWithoutEscalate.diagnostic,
|
|
384
|
+
);
|
|
352
385
|
}
|
|
353
386
|
}
|
|
354
387
|
}
|
|
@@ -383,9 +416,14 @@ export async function runPublicCountersign(
|
|
|
383
416
|
instruction: parsed.instruction,
|
|
384
417
|
attachmentPaths: parsed.attachmentPaths,
|
|
385
418
|
...(parsed.project === undefined ? {} : { project: parsed.project }),
|
|
386
|
-
...(env.createRunId === undefined
|
|
419
|
+
...(env.createRunId === undefined
|
|
420
|
+
? {}
|
|
421
|
+
: { createRunId: env.createRunId }),
|
|
387
422
|
...(env.model === undefined ? {} : { model: env.model }),
|
|
388
|
-
...(env.correlationId === undefined
|
|
423
|
+
...(env.correlationId === undefined
|
|
424
|
+
? {}
|
|
425
|
+
: { correlationId: env.correlationId }),
|
|
426
|
+
deferPersistence: true,
|
|
389
427
|
});
|
|
390
428
|
} catch (error) {
|
|
391
429
|
if (error instanceof CliUsageError) {
|
|
@@ -395,199 +433,267 @@ export async function runPublicCountersign(
|
|
|
395
433
|
throw error;
|
|
396
434
|
}
|
|
397
435
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
436
|
+
// One owner spans prepare, lookup/load/resume or new-run materialization.
|
|
437
|
+
// It preserves both causes when its final cleanup also fails.
|
|
438
|
+
try {
|
|
439
|
+
return await withPreparedAttachments(
|
|
440
|
+
parsed.attachmentPaths,
|
|
441
|
+
async (preparedAttachments) => {
|
|
442
|
+
const materializeAdmission = async (): Promise<void> => {
|
|
443
|
+
await materializeCountersignInvocation(admitted, {
|
|
444
|
+
home: env.home,
|
|
445
|
+
principalAuthority: env.principalAuthority,
|
|
446
|
+
preparedAttachments,
|
|
447
|
+
...(env.model === undefined ? {} : { model: env.model }),
|
|
448
|
+
});
|
|
449
|
+
};
|
|
410
450
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
);
|
|
422
|
-
identityDiaristRan = true;
|
|
451
|
+
// #637 / #771 / ADR 0079: ticket identity is the 起居郎 LLM typed assertion
|
|
452
|
+
// (never mechanical matching of summons text). Resolve that typed key before
|
|
453
|
+
// materializing a run: same-ticket re-summons write only to the retained run.
|
|
454
|
+
// Controlled failures materialize below so they still have a durable page. The test
|
|
455
|
+
// seam `runCourtDiaristStation` defers identity to beforeDispatch; generic
|
|
456
|
+
// hook failures stay on the parent call-local budget, exhausted nested
|
|
457
|
+
// station children still skip parent auto-resume (#840 父子不层叠).
|
|
458
|
+
let typedTicket: number | undefined;
|
|
459
|
+
let typedCourtTicketNumbers: readonly number[] | undefined;
|
|
460
|
+
let identityDiaristRan = false;
|
|
423
461
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
462
|
+
if (env.runCourtDiaristStation === undefined) {
|
|
463
|
+
let outcome: CourtDiaristInvocationResult;
|
|
464
|
+
try {
|
|
465
|
+
// The identity child cannot name the newly minted countersign id as parent:
|
|
466
|
+
// same-ticket lookup may intentionally never materialize that run. A selected
|
|
467
|
+
// retained run receives its normally correlated refresh child during resume.
|
|
468
|
+
outcome = await invokeCourtDiarist(
|
|
469
|
+
{
|
|
470
|
+
instruction: parsed.instruction,
|
|
471
|
+
projectRoot: admitted.projectRoot,
|
|
472
|
+
failureLabel: "unbound summons",
|
|
473
|
+
},
|
|
474
|
+
env,
|
|
475
|
+
io,
|
|
476
|
+
);
|
|
477
|
+
} catch (error) {
|
|
478
|
+
await materializeAdmission();
|
|
479
|
+
await markRunAdmitted(admitted, env.principalAuthority);
|
|
480
|
+
return await presentControlledFailure(
|
|
481
|
+
admitted,
|
|
482
|
+
{ timedOut: false, code: null, stderr: "", thrown: error },
|
|
483
|
+
countersignAdapters(),
|
|
484
|
+
env.principalAuthority,
|
|
485
|
+
io,
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
identityDiaristRan = true;
|
|
441
489
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
490
|
+
if (outcome.identity.kind === "escalate") {
|
|
491
|
+
// 御批: 识别不了就上抛 — materialize and settle this countersign run.
|
|
492
|
+
await materializeAdmission();
|
|
493
|
+
await markRunAdmitted(admitted, env.principalAuthority);
|
|
494
|
+
return await presentControlledFailure(
|
|
495
|
+
admitted,
|
|
496
|
+
{
|
|
497
|
+
timedOut: false,
|
|
498
|
+
code: null,
|
|
499
|
+
stderr: "",
|
|
500
|
+
thrown: new Error(
|
|
501
|
+
"court diarist station escalated (cannot identify court target)",
|
|
502
|
+
),
|
|
503
|
+
},
|
|
504
|
+
countersignAdapters(),
|
|
505
|
+
env.principalAuthority,
|
|
506
|
+
io,
|
|
507
|
+
);
|
|
508
|
+
}
|
|
459
509
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
summons,
|
|
480
|
-
resume: async (runId, materials) => {
|
|
481
|
-
// Resume selected: file the provisional run under the asserted ticket
|
|
482
|
-
// before abandoning it, so typed identity never leaves an unbound row.
|
|
483
|
-
await bindAdmittedTicketNumber(admitted, assertedTicket);
|
|
484
|
-
await relocateAdmittedRunToTicket(admitted, env.principalAuthority);
|
|
485
|
-
await markRunTerminal(admitted.runDirectory);
|
|
486
|
-
// Identity 起居郎 asserted unbound (no issue face). Resume still runs
|
|
487
|
-
// the bound refresh station under the typed key (ADR 0075: 每次过庭都跑是调用者用法).
|
|
488
|
-
// #871: hand the identity set so resume can whole-replace the run fact
|
|
489
|
-
// when this summons produced a new typed set (never union).
|
|
490
|
-
return await runPublicCountersignResume(
|
|
491
|
-
{
|
|
492
|
-
runId,
|
|
493
|
-
...(materials === undefined ? {} : { summons: materials }),
|
|
494
|
-
},
|
|
495
|
-
{
|
|
496
|
-
...env,
|
|
497
|
-
...(typedCourtTicketNumbers === undefined
|
|
498
|
-
? {}
|
|
499
|
-
: { pendingCourtTicketNumbers: typedCourtTicketNumbers }),
|
|
500
|
-
},
|
|
501
|
-
io,
|
|
502
|
-
);
|
|
503
|
-
},
|
|
504
|
-
});
|
|
505
|
-
if (resumed !== undefined) {
|
|
506
|
-
return resumed;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
+
// Typed failure terminal (verification / infra / non-zero without escalate)
|
|
511
|
+
// is not 真无票 — settle controlled failure on the admitted run (失败诚实).
|
|
512
|
+
// Only a true missing lawful typed terminal keeps the r5 unbound-continue.
|
|
513
|
+
if (outcome.failedWithoutEscalate !== undefined) {
|
|
514
|
+
await materializeAdmission();
|
|
515
|
+
await markRunAdmitted(admitted, env.principalAuthority);
|
|
516
|
+
return await presentControlledFailure(
|
|
517
|
+
admitted,
|
|
518
|
+
{
|
|
519
|
+
timedOut: false,
|
|
520
|
+
code: null,
|
|
521
|
+
stderr: "",
|
|
522
|
+
thrown: new Error(outcome.failedWithoutEscalate.diagnostic),
|
|
523
|
+
},
|
|
524
|
+
countersignAdapters(),
|
|
525
|
+
env.principalAuthority,
|
|
526
|
+
io,
|
|
527
|
+
);
|
|
528
|
+
}
|
|
510
529
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
530
|
+
// Missing lawful 起居郎 terminal is not countersign body failure: leave
|
|
531
|
+
// unbound and continue (true-unbound face). Escalate / typed failure above;
|
|
532
|
+
// bound refresh still fails honest via runCountersignCourtDiaristStation.
|
|
533
|
+
if (outcome.identity.kind === "ticket") {
|
|
534
|
+
const assertedTicket = outcome.identity.ticketNumber;
|
|
535
|
+
typedTicket = assertedTicket;
|
|
536
|
+
// #871: identity may hand a co-review set; absent field defaults to [main].
|
|
537
|
+
typedCourtTicketNumbers = outcome.identity.courtTicketNumbers;
|
|
538
|
+
const summons: SameTicketSummonsMaterials = {
|
|
539
|
+
instruction: parsed.instruction,
|
|
540
|
+
instructionEmpty: parsed.instruction.trim() === "",
|
|
541
|
+
};
|
|
542
|
+
const resumed = await tryResumeSameTicketSeatRun({
|
|
543
|
+
home: env.home,
|
|
544
|
+
projectRoot: admitted.projectRoot,
|
|
545
|
+
role: "countersign",
|
|
546
|
+
ticketNumber: typedTicket,
|
|
547
|
+
freshSummons: env.freshSummons,
|
|
548
|
+
summons,
|
|
549
|
+
resume: async (runId, materials) => {
|
|
550
|
+
// Consume the same pre-identity snapshot into the retained run; never
|
|
551
|
+
// reopen caller paths after identity has run.
|
|
552
|
+
const retained = await loadResumableCountersignRun(
|
|
553
|
+
env.home,
|
|
554
|
+
runId,
|
|
555
|
+
env.principalAuthority,
|
|
556
|
+
);
|
|
557
|
+
if (retained.admitted === undefined) {
|
|
558
|
+
throw new Error(
|
|
559
|
+
`retained countersign run disappeared before resume: ${runId}`,
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
const frozenPaths = (
|
|
563
|
+
await freezePreparedAttachmentsIntoRun(
|
|
564
|
+
preparedAttachments,
|
|
565
|
+
retained.admitted.runDirectory,
|
|
566
|
+
`s-${Date.now().toString(36)}`,
|
|
567
|
+
)
|
|
568
|
+
).map((attachment) => attachment.frozenPath);
|
|
569
|
+
const preparedMaterials: SameTicketSummonsMaterials = {
|
|
570
|
+
...(materials ?? {}),
|
|
571
|
+
...(frozenPaths.length === 0
|
|
572
|
+
? {}
|
|
573
|
+
: { attachmentPaths: frozenPaths }),
|
|
574
|
+
};
|
|
575
|
+
// Identity 起居郎 asserted unbound (no issue face). Resume still runs
|
|
576
|
+
// the bound refresh station under the typed key (ADR 0075: 每次过庭都跑是调用者用法).
|
|
577
|
+
// #871: hand the identity set so resume can whole-replace the run fact
|
|
578
|
+
// when this summons produced a new typed set (never union).
|
|
579
|
+
return await runPublicCountersignResume(
|
|
580
|
+
{
|
|
581
|
+
runId,
|
|
582
|
+
summons: preparedMaterials,
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
...env,
|
|
586
|
+
...(typedCourtTicketNumbers === undefined
|
|
587
|
+
? {}
|
|
588
|
+
: { pendingCourtTicketNumbers: typedCourtTicketNumbers }),
|
|
589
|
+
},
|
|
590
|
+
io,
|
|
591
|
+
);
|
|
592
|
+
},
|
|
593
|
+
});
|
|
594
|
+
if (resumed !== undefined) {
|
|
595
|
+
return resumed;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
537
599
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
...(env.model === undefined ? {} : { model: env.model }),
|
|
543
|
-
...pickEngineAxis(env),
|
|
544
|
-
...(env.timeoutMs === undefined ? {} : { timeoutMs: env.timeoutMs }),
|
|
545
|
-
...(env.correlationId === undefined || env.correlationId.trim() === ""
|
|
546
|
-
? {}
|
|
547
|
-
: { correlationId: env.correlationId }),
|
|
548
|
-
continuation: {
|
|
549
|
-
kind: "initial",
|
|
550
|
-
prompt: buildCountersignTransportPrompt(
|
|
551
|
-
admitted,
|
|
552
|
-
engineSessionMaterialFromOptions({
|
|
553
|
-
...pickEngineAxis(env),
|
|
554
|
-
packageRoot: env.packageRoot,
|
|
555
|
-
}),
|
|
556
|
-
),
|
|
557
|
-
},
|
|
558
|
-
};
|
|
559
|
-
// Mutable shell: ticket bind re-projects activation before executeTurn.
|
|
560
|
-
const turnRequest = buildCountersignTurnRequest(admitted, turnProjection);
|
|
600
|
+
// No prior run was selected. Materialize this invocation now; true-unbound,
|
|
601
|
+
// first-ticket and deferred test-seam paths all retain their own durable page.
|
|
602
|
+
await materializeAdmission();
|
|
603
|
+
await markRunAdmitted(admitted, env.principalAuthority);
|
|
561
604
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
605
|
+
if (identityDiaristRan && typedTicket !== undefined) {
|
|
606
|
+
try {
|
|
607
|
+
await bindAdmittedTicketNumber(admitted, typedTicket);
|
|
608
|
+
await relocateAdmittedRunToTicket(admitted, env.principalAuthority);
|
|
609
|
+
// First court: explicit set wins; omitted field → single-ticket face [main].
|
|
610
|
+
// Set persistence is outside beforeDispatch — route write failures into the
|
|
611
|
+
// same controlled-failure settlement as station children (#871 B7).
|
|
612
|
+
await bindCourtTicketNumbersOnAdmitted(
|
|
613
|
+
admitted,
|
|
614
|
+
typedCourtTicketNumbers ?? [typedTicket],
|
|
615
|
+
);
|
|
616
|
+
} catch (error) {
|
|
617
|
+
return await presentControlledFailure(
|
|
618
|
+
admitted,
|
|
619
|
+
{
|
|
620
|
+
timedOut: false,
|
|
621
|
+
code: null,
|
|
622
|
+
stderr: "",
|
|
623
|
+
thrown: error,
|
|
624
|
+
},
|
|
625
|
+
countersignAdapters(),
|
|
626
|
+
env.principalAuthority,
|
|
627
|
+
io,
|
|
628
|
+
);
|
|
629
|
+
}
|
|
575
630
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
env.
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
631
|
+
|
|
632
|
+
const turnProjection: RoleTurnRequestProjectionOptions = {
|
|
633
|
+
packageRoot: env.packageRoot,
|
|
634
|
+
home: env.home,
|
|
635
|
+
agentDir: env.agentDir,
|
|
636
|
+
...(env.model === undefined ? {} : { model: env.model }),
|
|
637
|
+
...pickEngineAxis(env),
|
|
638
|
+
...(env.timeoutMs === undefined ? {} : { timeoutMs: env.timeoutMs }),
|
|
639
|
+
...(env.correlationId === undefined || env.correlationId.trim() === ""
|
|
640
|
+
? {}
|
|
641
|
+
: { correlationId: env.correlationId }),
|
|
642
|
+
continuation: {
|
|
643
|
+
kind: "initial",
|
|
644
|
+
prompt: buildCountersignTransportPrompt(
|
|
645
|
+
admitted,
|
|
646
|
+
engineSessionMaterialFromOptions({
|
|
647
|
+
...pickEngineAxis(env),
|
|
648
|
+
packageRoot: env.packageRoot,
|
|
649
|
+
}),
|
|
650
|
+
),
|
|
651
|
+
},
|
|
652
|
+
};
|
|
653
|
+
// Mutable shell: ticket bind re-projects activation before executeTurn.
|
|
654
|
+
const turnRequest = buildCountersignTurnRequest(
|
|
655
|
+
admitted,
|
|
656
|
+
turnProjection,
|
|
584
657
|
);
|
|
658
|
+
|
|
659
|
+
const result = await runPostAdmissionOneShot({
|
|
660
|
+
admitted,
|
|
661
|
+
env,
|
|
662
|
+
io,
|
|
663
|
+
request: turnRequest,
|
|
664
|
+
adapters: countersignAdapters({
|
|
665
|
+
beforeDispatch: async (admittedSeat, lease) => {
|
|
666
|
+
// Dossier pointer delivery rides post-admission after this hook (#709).
|
|
667
|
+
if (!identityDiaristRan) {
|
|
668
|
+
// Test seam (or any deferred identity): station owns assert + bind.
|
|
669
|
+
await runCountersignCourtDiaristStation(admittedSeat, env, io);
|
|
670
|
+
} else if (typedTicket !== undefined) {
|
|
671
|
+
await runCountersignCourtDiaristStation(admittedSeat, env, io);
|
|
672
|
+
}
|
|
673
|
+
await relocateAdmittedRunToTicket(
|
|
674
|
+
admittedSeat,
|
|
675
|
+
env.principalAuthority,
|
|
676
|
+
lease,
|
|
677
|
+
);
|
|
678
|
+
Object.assign(
|
|
679
|
+
turnRequest,
|
|
680
|
+
buildCountersignTurnRequest(admittedSeat, turnProjection),
|
|
681
|
+
);
|
|
682
|
+
},
|
|
683
|
+
}),
|
|
684
|
+
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
685
|
+
});
|
|
686
|
+
await relocateAdmittedRunToTicket(admitted, env.principalAuthority);
|
|
687
|
+
return result;
|
|
585
688
|
},
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
689
|
+
);
|
|
690
|
+
} catch (error) {
|
|
691
|
+
if (error instanceof CliUsageError) {
|
|
692
|
+
presentStructuralRejection(error, io);
|
|
693
|
+
return { exitCode: 2 };
|
|
694
|
+
}
|
|
695
|
+
throw error;
|
|
696
|
+
}
|
|
591
697
|
}
|
|
592
698
|
|
|
593
699
|
function countersignAdapters(options?: {
|
|
@@ -658,7 +764,9 @@ export async function runPublicCountersignResume(
|
|
|
658
764
|
// #871 B7: durable set damage already identified at load — settle as
|
|
659
765
|
// station-child exhausted → presentControlledFailure (not structural exit 2).
|
|
660
766
|
if (admitted.courtTicketNumbersDamage !== undefined) {
|
|
661
|
-
throw new StationChildExhaustedError(
|
|
767
|
+
throw new StationChildExhaustedError(
|
|
768
|
+
admitted.courtTicketNumbersDamage,
|
|
769
|
+
);
|
|
662
770
|
}
|
|
663
771
|
// #871: same-ticket re-summons may hand a fresh typed set from identity.
|
|
664
772
|
// Whole-set replace onto this run fact; no new set → keep stored set.
|