@akagilnc/pi-workflow-roles 0.1.4062 → 0.1.4196
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 +2075 -1088
- package/dist/archivist-record-entry.js +204 -20
- package/dist/archivist-record-topology.js +36 -16
- package/dist/auditor-dossier-tool.js +2 -1
- package/dist/diarist-contracts.js +96 -0
- package/dist/doctor-evidence.js +3 -2
- package/dist/headless-host/production-host.js +2215 -1228
- package/dist/migrate-book-topology.js +278917 -0
- package/dist/navigator-public-session.js +22 -25
- package/dist/notary-source-run.js +7 -4
- package/dist/pi/durable-principal.js +9 -6
- package/dist/public-cli/auto-resume.js +2 -5
- package/dist/public-cli/diarist-run.js +27 -25
- package/dist/public-cli/instruction-seat-run.js +8 -11
- package/dist/public-cli/invocation.js +127 -63
- package/dist/public-cli/main.js +1507 -645
- package/dist/public-cli/option-definitions.js +2 -2
- package/dist/public-cli/post-admission.js +47 -12
- package/dist/public-cli/run-lifecycle.js +206 -58
- package/dist/public-cli/seat-ticket-binding.js +3 -6
- package/dist/public-cli/settlement.js +5 -3
- package/dist/role-run-placement.js +84 -0
- package/dist/role-run-relocation.js +358 -0
- package/dist/run-terminal-artifacts.js +2 -1
- package/dist/run-ticket-number.js +51 -32
- package/dist/sitian-appender.js +11 -26
- package/dist/submission-ledger.js +78 -19
- package/dist/ticket-provenance.js +3 -1
- package/package.json +3 -2
- package/scripts/build-package.mjs +34 -2
- package/souls/countersign.md +2 -2
- package/souls/fixer.md +3 -2
- package/src/analyst-ledger.ts +14 -15
- package/src/archivist-record-entry.ts +285 -38
- package/src/archivist-record-topology.ts +43 -18
- package/src/auditor-dossier-tool.ts +2 -1
- package/src/book-topology-migration-placement.ts +493 -0
- package/src/book-topology-migration.ts +220 -0
- package/src/book-topology-migrators-t11.ts +856 -0
- package/src/book-topology-mixed-volume-migrators.ts +470 -0
- package/src/book-topology-partition-migrators.ts +25 -0
- package/src/book-topology-record-class-migrators.ts +779 -0
- package/src/book-topology-runs-migrator.ts +244 -0
- package/src/diarist-contracts.ts +116 -0
- package/src/diarist-role.ts +6 -0
- package/src/diarist.ts +2 -0
- package/src/doctor-evidence.ts +4 -1
- package/src/doctor-role.ts +1 -1
- package/src/navigator-public-session.ts +33 -30
- package/src/notary-source-run.ts +9 -4
- package/src/pi/durable-principal.ts +9 -13
- package/src/public-cli/auto-resume.ts +2 -6
- package/src/public-cli/countersign-run.ts +182 -32
- package/src/public-cli/diarist-run.ts +34 -26
- package/src/public-cli/instruction-seat-run.ts +7 -11
- package/src/public-cli/invocation.ts +192 -58
- package/src/public-cli/option-definitions.ts +2 -2
- package/src/public-cli/post-admission.ts +73 -13
- package/src/public-cli/run-lifecycle.ts +255 -65
- package/src/public-cli/seat-ticket-binding.ts +3 -6
- package/src/public-cli/settlement.ts +6 -4
- package/src/role-run-placement.ts +128 -0
- package/src/role-run-relocation.ts +491 -0
- package/src/role-runtime.ts +11 -5
- package/src/run-terminal-artifacts.ts +3 -1
- package/src/run-ticket-number.ts +80 -20
- package/src/sitian-appender.ts +25 -28
- package/src/submission-ledger.ts +102 -19
- package/src/ticket-provenance.ts +4 -1
- package/src/ticket-trajectory.ts +22 -35
- package/src/worker-role.ts +4 -4
- package/src/worker-submission-gates.ts +6 -6
|
@@ -25,10 +25,16 @@
|
|
|
25
25
|
import type { DurablePrincipalAuthority, RoleTurnRequest } from "../host-contracts.ts";
|
|
26
26
|
import { engineSessionMaterialFromOptions, pickEngineAxis } from "../package-resources/engine-material.ts";
|
|
27
27
|
import { CliUsageError } from "./cli-errors.ts";
|
|
28
|
+
import {
|
|
29
|
+
projectCourtTicketNumbers,
|
|
30
|
+
} from "../diarist-contracts.ts";
|
|
31
|
+
import { isSafePositiveTicketNumber } from "../run-ticket-number.ts";
|
|
28
32
|
import {
|
|
29
33
|
admitCountersignInvocation,
|
|
30
34
|
bindAdmittedTicketNumber,
|
|
35
|
+
bindCourtTicketNumbersOnAdmitted,
|
|
31
36
|
buildCountersignTransportPrompt,
|
|
37
|
+
relocateAdmittedRunToTicket,
|
|
32
38
|
type AdmittedCountersignInvocation,
|
|
33
39
|
type ParseCountersignArgvResult,
|
|
34
40
|
} from "./invocation.ts";
|
|
@@ -46,6 +52,7 @@ import {
|
|
|
46
52
|
markRunAdmitted,
|
|
47
53
|
markRunTerminal,
|
|
48
54
|
type PublicResumeRequest,
|
|
55
|
+
type RunWriterLease,
|
|
49
56
|
type SameTicketSummonsMaterials,
|
|
50
57
|
} from "./run-lifecycle.ts";
|
|
51
58
|
import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.ts";
|
|
@@ -70,6 +77,12 @@ export type CountersignRunEnv = PostAdmissionEnv & {
|
|
|
70
77
|
runCourtDiaristStation?: (
|
|
71
78
|
admitted: AdmittedCountersignInvocation,
|
|
72
79
|
) => Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* #871: typed co-review set from the identity 起居郎 turn, applied onto the
|
|
82
|
+
* resumed run before bound refresh (same-ticket re-summons path). Whole-set
|
|
83
|
+
* replace — never union. Production leaves unset outside that handoff.
|
|
84
|
+
*/
|
|
85
|
+
pendingCourtTicketNumbers?: readonly number[];
|
|
73
86
|
};
|
|
74
87
|
|
|
75
88
|
/** Project admitted invocation onto the host-neutral turn request. */
|
|
@@ -94,7 +107,16 @@ export function buildCountersignTurnRequest(
|
|
|
94
107
|
|
|
95
108
|
/** 起居郎 identity outcome — escalate stays distinct from missing terminal. */
|
|
96
109
|
type CourtDiaristIdentity =
|
|
97
|
-
| {
|
|
110
|
+
| {
|
|
111
|
+
readonly kind: "ticket";
|
|
112
|
+
readonly ticketNumber: number;
|
|
113
|
+
/**
|
|
114
|
+
* #871 typed co-review set when the LLM explicitly submitted one.
|
|
115
|
+
* Absent means "no new set this turn" — resume must keep the stored run fact
|
|
116
|
+
* (never silently degrade a multi-ticket set to [main]).
|
|
117
|
+
*/
|
|
118
|
+
readonly courtTicketNumbers?: readonly number[];
|
|
119
|
+
}
|
|
98
120
|
| { readonly kind: "unbound" }
|
|
99
121
|
| { readonly kind: "escalate" };
|
|
100
122
|
|
|
@@ -103,6 +125,53 @@ type CourtDiaristInvocationResult = {
|
|
|
103
125
|
readonly failedWithoutEscalate?: { readonly diagnostic: string };
|
|
104
126
|
};
|
|
105
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Read #871 set from preserved diarist payloads.
|
|
130
|
+
* - Field absent / null / non-array → undefined (no new set; resume keeps store).
|
|
131
|
+
* - Explicit [] → single-ticket [main] whole-set replace (signed empty-set contract).
|
|
132
|
+
* - Non-empty array with zero lawful typed members after projection → not a new set
|
|
133
|
+
* (do not impersonate explicit empty and wipe a stored multi-ticket fact).
|
|
134
|
+
* - Non-empty array with ≥1 lawful member → sole type/dedupe projection + principal guarantee.
|
|
135
|
+
* - Multiple submissions: last qualifying set wins.
|
|
136
|
+
* Live path never shape-rejects the role turn; durable damage is a separate seam.
|
|
137
|
+
*/
|
|
138
|
+
function courtTicketNumbersFromOutcome(
|
|
139
|
+
roleOutcome: TerminalRoleOutcome | undefined,
|
|
140
|
+
principalTicket: number,
|
|
141
|
+
): readonly number[] | undefined {
|
|
142
|
+
if (roleOutcome === undefined) return undefined;
|
|
143
|
+
const payloads =
|
|
144
|
+
roleOutcome.kind === "accepted" ||
|
|
145
|
+
roleOutcome.kind === "audit_escalation" ||
|
|
146
|
+
roleOutcome.kind === "failure"
|
|
147
|
+
? roleOutcome.payloads ?? []
|
|
148
|
+
: [];
|
|
149
|
+
let latest: readonly number[] | undefined;
|
|
150
|
+
for (const payload of payloads) {
|
|
151
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const record = payload as Record<string, unknown>;
|
|
155
|
+
if (!Object.hasOwn(record, "courtTicketNumbers")) continue;
|
|
156
|
+
const raw = record.courtTicketNumbers;
|
|
157
|
+
// Only a real array is a candidate set. Non-array is not explicit empty.
|
|
158
|
+
if (!Array.isArray(raw)) continue;
|
|
159
|
+
// Members only — do not inject principal yet, or all-invalid non-empty becomes [main].
|
|
160
|
+
const members = projectCourtTicketNumbers(raw);
|
|
161
|
+
if (members === null) continue;
|
|
162
|
+
if (members.length === 0) {
|
|
163
|
+
// Literal [] is the intentional principal-only replace; non-empty garbage is not.
|
|
164
|
+
if (raw.length === 0) {
|
|
165
|
+
latest = projectCourtTicketNumbers([], { principalTicket }) ?? undefined;
|
|
166
|
+
}
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
latest =
|
|
170
|
+
projectCourtTicketNumbers(raw, { principalTicket }) ?? undefined;
|
|
171
|
+
}
|
|
172
|
+
return latest;
|
|
173
|
+
}
|
|
174
|
+
|
|
106
175
|
/** Routing boolean over the child's own typed sequence — does not pick or rewrite a sole row. */
|
|
107
176
|
function courtDiaristEscalated(roleOutcome: TerminalRoleOutcome | undefined): boolean {
|
|
108
177
|
if (roleOutcome === undefined) return false;
|
|
@@ -181,13 +250,14 @@ async function invokeCourtDiarist(input: {
|
|
|
181
250
|
}
|
|
182
251
|
|
|
183
252
|
const asserted = (result.admitted as { ticketNumber?: number } | undefined)?.ticketNumber;
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
Number.isSafeInteger(asserted) &&
|
|
187
|
-
asserted >= 1
|
|
188
|
-
) {
|
|
253
|
+
if (isSafePositiveTicketNumber(asserted)) {
|
|
254
|
+
const courtTicketNumbers = courtTicketNumbersFromOutcome(roleOutcome, asserted);
|
|
189
255
|
return {
|
|
190
|
-
identity: {
|
|
256
|
+
identity: {
|
|
257
|
+
kind: "ticket",
|
|
258
|
+
ticketNumber: asserted,
|
|
259
|
+
...(courtTicketNumbers === undefined ? {} : { courtTicketNumbers }),
|
|
260
|
+
},
|
|
191
261
|
};
|
|
192
262
|
}
|
|
193
263
|
return {
|
|
@@ -219,25 +289,45 @@ export async function runCountersignCourtDiaristStation(
|
|
|
219
289
|
// First-entry unbound identity is owned by runPublicCountersign.
|
|
220
290
|
if (admitted.ticketNumber === undefined) return;
|
|
221
291
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
292
|
+
// #871: refresh every member of the typed co-review set; single-ticket face
|
|
293
|
+
// is just the set [main]. Present-but-empty / missing-principal is damage —
|
|
294
|
+
// never silently fall back to main-only (legacy absent field still may).
|
|
295
|
+
let refreshTickets: readonly number[];
|
|
296
|
+
if (admitted.courtTicketNumbers !== undefined) {
|
|
297
|
+
if (
|
|
298
|
+
admitted.courtTicketNumbers.length === 0 ||
|
|
299
|
+
!admitted.courtTicketNumbers.includes(admitted.ticketNumber)
|
|
300
|
+
) {
|
|
301
|
+
throw new StationChildExhaustedError(
|
|
302
|
+
`court diarist station: courtTicketNumbers is damaged (empty or missing principal #${admitted.ticketNumber})`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
refreshTickets = admitted.courtTicketNumbers;
|
|
306
|
+
} else {
|
|
307
|
+
refreshTickets = [admitted.ticketNumber];
|
|
308
|
+
}
|
|
233
309
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
310
|
+
for (const ticketNumber of refreshTickets) {
|
|
311
|
+
const outcome = await invokeCourtDiarist(
|
|
312
|
+
{
|
|
313
|
+
instruction: `整理 #${ticketNumber} 的本案依据。`,
|
|
314
|
+
projectRoot: admitted.projectRoot,
|
|
315
|
+
failureLabel: `ticket #${ticketNumber}`,
|
|
316
|
+
// Refresh holds a typed key — hand it off so identity is bound before turn.
|
|
317
|
+
boundTicketNumber: ticketNumber,
|
|
318
|
+
},
|
|
319
|
+
env,
|
|
320
|
+
io,
|
|
237
321
|
);
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
322
|
+
|
|
323
|
+
if (outcome.identity.kind === "escalate") {
|
|
324
|
+
throw new StationChildExhaustedError(
|
|
325
|
+
"court diarist station escalated (cannot identify court target)",
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
if (outcome.failedWithoutEscalate !== undefined) {
|
|
329
|
+
throw new StationChildExhaustedError(outcome.failedWithoutEscalate.diagnostic);
|
|
330
|
+
}
|
|
241
331
|
}
|
|
242
332
|
}
|
|
243
333
|
|
|
@@ -293,6 +383,7 @@ export async function runPublicCountersign(
|
|
|
293
383
|
// hook failures stay on the parent call-local budget, exhausted nested
|
|
294
384
|
// station children still skip parent auto-resume (#840 父子不层叠).
|
|
295
385
|
let typedTicket: number | undefined;
|
|
386
|
+
let typedCourtTicketNumbers: readonly number[] | undefined;
|
|
296
387
|
let identityDiaristRan = false;
|
|
297
388
|
|
|
298
389
|
if (env.runCourtDiaristStation === undefined) {
|
|
@@ -347,7 +438,10 @@ export async function runPublicCountersign(
|
|
|
347
438
|
// unbound and continue (true-unbound face). Escalate / typed failure above;
|
|
348
439
|
// bound refresh still fails honest via runCountersignCourtDiaristStation.
|
|
349
440
|
if (outcome.identity.kind === "ticket") {
|
|
350
|
-
|
|
441
|
+
const assertedTicket = outcome.identity.ticketNumber;
|
|
442
|
+
typedTicket = assertedTicket;
|
|
443
|
+
// #871: identity may hand a co-review set; absent field defaults to [main].
|
|
444
|
+
typedCourtTicketNumbers = outcome.identity.courtTicketNumbers;
|
|
351
445
|
const summons: SameTicketSummonsMaterials = {
|
|
352
446
|
instruction: parsed.instruction,
|
|
353
447
|
instructionEmpty: parsed.instruction.trim() === "",
|
|
@@ -361,16 +455,26 @@ export async function runPublicCountersign(
|
|
|
361
455
|
freshSummons: env.freshSummons,
|
|
362
456
|
summons,
|
|
363
457
|
resume: async (runId, materials) => {
|
|
364
|
-
// Resume selected:
|
|
458
|
+
// Resume selected: file the provisional run under the asserted ticket
|
|
459
|
+
// before abandoning it, so typed identity never leaves an unbound row.
|
|
460
|
+
await bindAdmittedTicketNumber(admitted, assertedTicket);
|
|
461
|
+
await relocateAdmittedRunToTicket(admitted, env.principalAuthority);
|
|
365
462
|
await markRunTerminal(admitted.runDirectory);
|
|
366
463
|
// Identity 起居郎 asserted unbound (no issue face). Resume still runs
|
|
367
464
|
// the bound refresh station under the typed key (refresh-every-court).
|
|
465
|
+
// #871: hand the identity set so resume can whole-replace the run fact
|
|
466
|
+
// when this summons produced a new typed set (never union).
|
|
368
467
|
return await runPublicCountersignResume(
|
|
369
468
|
{
|
|
370
469
|
runId,
|
|
371
470
|
...(materials === undefined ? {} : { summons: materials }),
|
|
372
471
|
},
|
|
373
|
-
|
|
472
|
+
{
|
|
473
|
+
...env,
|
|
474
|
+
...(typedCourtTicketNumbers === undefined
|
|
475
|
+
? {}
|
|
476
|
+
: { pendingCourtTicketNumbers: typedCourtTicketNumbers }),
|
|
477
|
+
},
|
|
374
478
|
io,
|
|
375
479
|
);
|
|
376
480
|
},
|
|
@@ -381,6 +485,33 @@ export async function runPublicCountersign(
|
|
|
381
485
|
}
|
|
382
486
|
}
|
|
383
487
|
|
|
488
|
+
if (identityDiaristRan && typedTicket !== undefined) {
|
|
489
|
+
try {
|
|
490
|
+
await bindAdmittedTicketNumber(admitted, typedTicket);
|
|
491
|
+
await relocateAdmittedRunToTicket(admitted, env.principalAuthority);
|
|
492
|
+
// First court: explicit set wins; omitted field → single-ticket face [main].
|
|
493
|
+
// Set persistence is outside beforeDispatch — route write failures into the
|
|
494
|
+
// same controlled-failure settlement as station children (#871 B7).
|
|
495
|
+
await bindCourtTicketNumbersOnAdmitted(
|
|
496
|
+
admitted,
|
|
497
|
+
typedCourtTicketNumbers ?? [typedTicket],
|
|
498
|
+
);
|
|
499
|
+
} catch (error) {
|
|
500
|
+
return await presentControlledFailure(
|
|
501
|
+
admitted,
|
|
502
|
+
{
|
|
503
|
+
timedOut: false,
|
|
504
|
+
code: null,
|
|
505
|
+
stderr: "",
|
|
506
|
+
thrown: error,
|
|
507
|
+
},
|
|
508
|
+
countersignAdapters(),
|
|
509
|
+
env.principalAuthority,
|
|
510
|
+
io,
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
384
515
|
const turnProjection: RoleTurnRequestProjectionOptions = {
|
|
385
516
|
packageRoot: env.packageRoot,
|
|
386
517
|
home: env.home,
|
|
@@ -405,23 +536,25 @@ export async function runPublicCountersign(
|
|
|
405
536
|
// Mutable shell: ticket bind re-projects activation before executeTurn.
|
|
406
537
|
const turnRequest = buildCountersignTurnRequest(admitted, turnProjection);
|
|
407
538
|
|
|
408
|
-
|
|
539
|
+
const result = await runPostAdmissionOneShot({
|
|
409
540
|
admitted,
|
|
410
541
|
env,
|
|
411
542
|
io,
|
|
412
543
|
request: turnRequest,
|
|
413
544
|
adapters: countersignAdapters({
|
|
414
|
-
beforeDispatch: async (admittedSeat) => {
|
|
545
|
+
beforeDispatch: async (admittedSeat, lease) => {
|
|
415
546
|
// Dossier pointer delivery rides post-admission after this hook (#709).
|
|
416
547
|
if (!identityDiaristRan) {
|
|
417
548
|
// Test seam (or any deferred identity): station owns assert + bind.
|
|
418
549
|
await runCountersignCourtDiaristStation(admittedSeat, env, io);
|
|
419
550
|
} else if (typedTicket !== undefined) {
|
|
420
|
-
// Production identity asserted unbound; bind typed key, then bound
|
|
421
|
-
// refresh so freeze loads issue face (typed handoff, not prose match).
|
|
422
|
-
await bindAdmittedTicketNumber(admittedSeat, typedTicket);
|
|
423
551
|
await runCountersignCourtDiaristStation(admittedSeat, env, io);
|
|
424
552
|
}
|
|
553
|
+
await relocateAdmittedRunToTicket(
|
|
554
|
+
admittedSeat,
|
|
555
|
+
env.principalAuthority,
|
|
556
|
+
lease,
|
|
557
|
+
);
|
|
425
558
|
Object.assign(
|
|
426
559
|
turnRequest,
|
|
427
560
|
buildCountersignTurnRequest(admittedSeat, turnProjection),
|
|
@@ -430,11 +563,14 @@ export async function runPublicCountersign(
|
|
|
430
563
|
}),
|
|
431
564
|
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
432
565
|
});
|
|
566
|
+
await relocateAdmittedRunToTicket(admitted, env.principalAuthority);
|
|
567
|
+
return result;
|
|
433
568
|
}
|
|
434
569
|
|
|
435
570
|
function countersignAdapters(options?: {
|
|
436
571
|
beforeDispatch?: (
|
|
437
572
|
admitted: AdmittedCountersignInvocation,
|
|
573
|
+
lease: RunWriterLease,
|
|
438
574
|
) => void | Promise<void>;
|
|
439
575
|
}) {
|
|
440
576
|
return {
|
|
@@ -496,6 +632,20 @@ export async function runPublicCountersignResume(
|
|
|
496
632
|
},
|
|
497
633
|
adapters: countersignAdapters({
|
|
498
634
|
beforeDispatch: async (admitted) => {
|
|
635
|
+
// #871 B7: durable set damage already identified at load — settle as
|
|
636
|
+
// station-child exhausted → presentControlledFailure (not structural exit 2).
|
|
637
|
+
if (admitted.courtTicketNumbersDamage !== undefined) {
|
|
638
|
+
throw new StationChildExhaustedError(admitted.courtTicketNumbersDamage);
|
|
639
|
+
}
|
|
640
|
+
// #871: same-ticket re-summons may hand a fresh typed set from identity.
|
|
641
|
+
// Whole-set replace onto this run fact; no new set → keep stored set.
|
|
642
|
+
// Manual resume (no pending) keeps the durable set and never invents one.
|
|
643
|
+
if (env.pendingCourtTicketNumbers !== undefined) {
|
|
644
|
+
await bindCourtTicketNumbersOnAdmitted(
|
|
645
|
+
admitted,
|
|
646
|
+
env.pendingCourtTicketNumbers,
|
|
647
|
+
);
|
|
648
|
+
}
|
|
499
649
|
await runCountersignCourtDiaristStation(admitted, env, io);
|
|
500
650
|
},
|
|
501
651
|
}),
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
admitDiaristInvocation,
|
|
13
13
|
bindAdmittedTicketNumber,
|
|
14
14
|
buildInstructionTransportPrompt,
|
|
15
|
+
relocateAdmittedRunToTicket,
|
|
15
16
|
type AdmittedDiaristInvocation,
|
|
16
17
|
type ParseDiaristArgvResult,
|
|
17
18
|
} from "./invocation.ts";
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
loadResumableDiaristRun,
|
|
28
29
|
markRunAdmitted,
|
|
29
30
|
type PublicResumeRequest,
|
|
31
|
+
type RunWriterLease,
|
|
30
32
|
type SameTicketSummonsMaterials,
|
|
31
33
|
} from "./run-lifecycle.ts";
|
|
32
34
|
import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.ts";
|
|
@@ -40,7 +42,7 @@ import {
|
|
|
40
42
|
projectRoleTurnRequest,
|
|
41
43
|
type RoleTurnRequestProjectionOptions,
|
|
42
44
|
} from "./turn-request.ts";
|
|
43
|
-
import {
|
|
45
|
+
import { readBoardTicketNumber } from "../run-ticket-number.ts";
|
|
44
46
|
|
|
45
47
|
export type DiaristRunEnv = PostAdmissionEnv & {
|
|
46
48
|
principalAuthority: DurablePrincipalAuthority;
|
|
@@ -66,11 +68,38 @@ export function buildDiaristTurnRequest(
|
|
|
66
68
|
);
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Board-bound ticket on pages → memory bind + unbound→ticket relocate under lease.
|
|
73
|
+
* Shared by resume (interrupted board-bound unbound) and post-turn first assert.
|
|
74
|
+
*/
|
|
75
|
+
async function bindAndRelocateDiaristIfBoardBound(
|
|
76
|
+
admitted: AdmittedDiaristInvocation,
|
|
77
|
+
authority: DiaristRunEnv["principalAuthority"],
|
|
78
|
+
lease: RunWriterLease,
|
|
79
|
+
): Promise<void> {
|
|
80
|
+
const boardTicket = await readBoardTicketNumber(admitted.runDirectory);
|
|
81
|
+
if (boardTicket === undefined) return;
|
|
82
|
+
if (admitted.ticketNumber === undefined) {
|
|
83
|
+
await bindAdmittedTicketNumber(admitted, boardTicket);
|
|
84
|
+
}
|
|
85
|
+
await relocateAdmittedRunToTicket(admitted, authority, lease);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function diaristAdapters(
|
|
89
|
+
env: DiaristRunEnv,
|
|
90
|
+
): PostAdmissionAdapters<AdmittedDiaristInvocation> {
|
|
70
91
|
return {
|
|
71
92
|
trySettle: (admitted, authority, scope) =>
|
|
72
93
|
trySettleDiaristTerminalResult(admitted, authority, scope),
|
|
73
94
|
shouldPresentSettled: () => true,
|
|
95
|
+
// Resume interrupted state: board ticket already on pages, run still under unbound/.
|
|
96
|
+
beforeDispatch: async (admitted, lease) => {
|
|
97
|
+
await bindAndRelocateDiaristIfBoardBound(admitted, env.principalAuthority, lease);
|
|
98
|
+
},
|
|
99
|
+
// First post-turn assert: accept hook wrote board pages; relocate before lease release.
|
|
100
|
+
afterDispatch: async (admitted, lease) => {
|
|
101
|
+
await bindAndRelocateDiaristIfBoardBound(admitted, env.principalAuthority, lease);
|
|
102
|
+
},
|
|
74
103
|
};
|
|
75
104
|
}
|
|
76
105
|
|
|
@@ -141,6 +170,7 @@ export async function runPublicDiarist(
|
|
|
141
170
|
...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
|
|
142
171
|
...(env.model === undefined ? {} : { model: env.model }),
|
|
143
172
|
...(env.correlationId === undefined ? {} : { correlationId: env.correlationId }),
|
|
173
|
+
...(handoffTicket === undefined ? {} : { assertedTicketNumber: handoffTicket }),
|
|
144
174
|
});
|
|
145
175
|
} catch (error) {
|
|
146
176
|
if (error instanceof CliUsageError) {
|
|
@@ -152,14 +182,6 @@ export async function runPublicDiarist(
|
|
|
152
182
|
|
|
153
183
|
await markRunAdmitted(admitted, env.principalAuthority);
|
|
154
184
|
|
|
155
|
-
// Typed handoff: bind before the turn so identity is on durable pages.
|
|
156
|
-
if (
|
|
157
|
-
typeof handoffTicket === "number" &&
|
|
158
|
-
Number.isSafeInteger(handoffTicket) &&
|
|
159
|
-
handoffTicket >= 1
|
|
160
|
-
) {
|
|
161
|
-
await bindAdmittedTicketNumber(admitted, handoffTicket);
|
|
162
|
-
}
|
|
163
185
|
|
|
164
186
|
const turnProjection: RoleTurnRequestProjectionOptions = {
|
|
165
187
|
packageRoot: env.packageRoot,
|
|
@@ -189,22 +211,8 @@ export async function runPublicDiarist(
|
|
|
189
211
|
env,
|
|
190
212
|
io,
|
|
191
213
|
request: turnRequest,
|
|
192
|
-
adapters: diaristAdapters(),
|
|
214
|
+
adapters: diaristAdapters(env),
|
|
193
215
|
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
194
|
-
}).then(async (result) => {
|
|
195
|
-
// Accept hook binds ticket onto durable pages (#771). Mirror that page fact
|
|
196
|
-
// onto the caller-visible admitted object — never pick a ticketNumber out of
|
|
197
|
-
// the payload sequence (#881 sole-collapse ban on ticket/escalate).
|
|
198
|
-
if (admitted.ticketNumber === undefined && result.admitted !== undefined) {
|
|
199
|
-
const fromPages = await readRunTicketNumber(admitted.runDirectory);
|
|
200
|
-
if (fromPages !== undefined) {
|
|
201
|
-
await bindAdmittedTicketNumber(admitted, fromPages);
|
|
202
|
-
if (result.admitted.ticketNumber === undefined) {
|
|
203
|
-
(result.admitted as { ticketNumber?: number }).ticketNumber = fromPages;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
return result;
|
|
208
216
|
});
|
|
209
217
|
}
|
|
210
218
|
|
|
@@ -242,7 +250,7 @@ export async function runPublicDiaristResume(
|
|
|
242
250
|
),
|
|
243
251
|
);
|
|
244
252
|
},
|
|
245
|
-
adapters: diaristAdapters(),
|
|
253
|
+
adapters: diaristAdapters(env),
|
|
246
254
|
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
247
255
|
});
|
|
248
256
|
}
|
|
@@ -7,13 +7,12 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { DurablePrincipalAuthority, RoleTurnRequest } from "../host-contracts.ts";
|
|
9
9
|
import { engineSessionMaterialFromOptions, pickEngineAxis } from "../package-resources/engine-material.ts";
|
|
10
|
-
import {
|
|
10
|
+
import { readBoardTicketNumber } from "../run-ticket-number.ts";
|
|
11
11
|
import { CliUsageError } from "./cli-errors.ts";
|
|
12
12
|
import {
|
|
13
13
|
admitAuditorInvocation,
|
|
14
14
|
admitGatekeeperInvocation,
|
|
15
15
|
admitNavigatorInvocation,
|
|
16
|
-
bindAdmittedTicketNumber,
|
|
17
16
|
buildInstructionTransportPrompt,
|
|
18
17
|
persistAdmittedSourceRunPath,
|
|
19
18
|
type AdmittedAuditorInvocation,
|
|
@@ -286,7 +285,8 @@ export async function runPublicInstructionSeat(
|
|
|
286
285
|
home: env.home,
|
|
287
286
|
});
|
|
288
287
|
auditorSourceRun = resolved.runDirectory;
|
|
289
|
-
|
|
288
|
+
// Auditor inherits board identity only — migration-derived is display, not admission.
|
|
289
|
+
auditorSourceTicket = await readBoardTicketNumber(resolved.runDirectory);
|
|
290
290
|
} catch (error) {
|
|
291
291
|
presentStructuralRejection(
|
|
292
292
|
new CliUsageError(error instanceof Error ? error.message : String(error)),
|
|
@@ -345,6 +345,9 @@ export async function runPublicInstructionSeat(
|
|
|
345
345
|
...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
|
|
346
346
|
...(env.model === undefined ? {} : { model: env.model }),
|
|
347
347
|
...(env.correlationId === undefined ? {} : { correlationId: env.correlationId }),
|
|
348
|
+
...(auditorSourceTicket === undefined
|
|
349
|
+
? {}
|
|
350
|
+
: { assertedTicketNumber: auditorSourceTicket }),
|
|
348
351
|
});
|
|
349
352
|
} catch (error) {
|
|
350
353
|
if (error instanceof CliUsageError) {
|
|
@@ -395,14 +398,7 @@ export async function runPublicInstructionSeat(
|
|
|
395
398
|
env,
|
|
396
399
|
io,
|
|
397
400
|
request: turnRequest,
|
|
398
|
-
adapters: instructionSeatAdapters(
|
|
399
|
-
beforeDispatch: async (admittedSeat) => {
|
|
400
|
-
// Auditor inherits source-run ticket (notary face) — typed, not prose match.
|
|
401
|
-
if (auditorSourceTicket !== undefined) {
|
|
402
|
-
await bindAdmittedTicketNumber(admittedSeat, auditorSourceTicket);
|
|
403
|
-
}
|
|
404
|
-
},
|
|
405
|
-
}),
|
|
401
|
+
adapters: instructionSeatAdapters(),
|
|
406
402
|
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
407
403
|
});
|
|
408
404
|
},
|