@akagilnc/pi-workflow-roles 0.1.5289 → 0.1.5301
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/README.md +7 -3
- package/README.zh-CN.md +7 -3
- package/dist/acp-host/production-host.js +97 -462
- package/dist/diarist-contracts.js +20 -96
- package/dist/headless-host/production-host.js +97 -462
- package/dist/migrate-book-topology.js +75 -77
- package/dist/public-cli/countersign-run.js +3 -124
- package/dist/public-cli/instruction-seat-run.js +5 -97
- package/dist/public-cli/invocation.js +0 -33
- package/dist/public-cli/main.js +10 -362
- package/dist/public-cli/post-admission.js +2 -14
- package/dist/public-cli/run-lifecycle.js +0 -85
- package/dist/public-role-summons.js +2 -2
- package/dist/ticket-provenance.js +25 -29
- package/package.json +1 -1
- package/resources/diarist-collect.md +4 -4
- package/src/diarist-contracts.ts +24 -116
- package/src/diarist-role.ts +39 -50
- package/src/diarist.ts +9 -29
- package/src/public-cli/countersign-run.ts +7 -184
- package/src/public-cli/instruction-seat-run.ts +6 -101
- package/src/public-cli/invocation.ts +2 -64
- package/src/public-cli/post-admission.ts +2 -15
- package/src/public-cli/run-lifecycle.ts +0 -117
- package/src/public-role-summons.ts +3 -3
- package/src/role-runtime.ts +13 -7
- package/src/ticket-provenance.ts +31 -44
|
@@ -1,40 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* instruction-seat-run. This module only summons 起居郎 and refreshes a bound
|
|
5
|
-
* court diary.
|
|
6
|
-
*
|
|
7
|
-
* Court admission auto-runs 起居郎 so the 起居郎 LLM asserts the court target;
|
|
8
|
-
* mechanical layer only verifies; countersign reuses that typed identity for bind
|
|
9
|
-
* (ADR 0075 / 0081). Code never matches instruction text against book-known
|
|
10
|
-
* numbers. Who may call 起居郎 and in what order is not written into law
|
|
11
|
-
* (ADR 0075 不规定谁调用起居郎、顺序归调用者). #1092: code no longer freezes or
|
|
12
|
-
* delivers 起居录 paths — roles locate records by ticket. Bound refresh hands the
|
|
13
|
-
* typed key to 起居郎 so freeze loads issue face (ADR 0075: 每次过庭都跑是调用者用法 /
|
|
14
|
-
* typed handoff).
|
|
2
|
+
* Secretariat's pre-ticket diarist summons. Countersign no longer summons
|
|
3
|
+
* the diarist; callers refresh the diary before court when needed (ADR 0081).
|
|
15
4
|
*/
|
|
16
5
|
import type { DurablePrincipalAuthority } from "../host-contracts.ts";
|
|
17
|
-
import { projectCourtTicketNumbers } from "../diarist-contracts.ts";
|
|
18
6
|
import { readableGateItem } from "../readable-gate-item.ts";
|
|
19
|
-
import { isSafePositiveTicketNumber } from "../run-ticket-number.ts";
|
|
20
7
|
import type { AdmittedCountersignInvocation } from "./invocation.ts";
|
|
21
|
-
import {
|
|
22
|
-
type PostAdmissionEnv,
|
|
23
|
-
StationChildExhaustedError,
|
|
24
|
-
} from "./post-admission.ts";
|
|
8
|
+
import type { PostAdmissionEnv } from "./post-admission.ts";
|
|
25
9
|
import type { CliIo } from "./cli-io.ts";
|
|
26
10
|
import type { TerminalRoleOutcome } from "./terminal.ts";
|
|
27
11
|
|
|
28
12
|
export type CountersignRunEnv = PostAdmissionEnv & {
|
|
29
13
|
principalAuthority: DurablePrincipalAuthority;
|
|
30
14
|
createRunId?: () => string;
|
|
31
|
-
/**
|
|
32
|
-
* Test seam: replace the court-pipeline 起居郎 station.
|
|
33
|
-
* Production leaves this unset and runs the public diarist seat.
|
|
34
|
-
*/
|
|
35
|
-
runCourtDiaristStation?: (
|
|
36
|
-
admitted: AdmittedCountersignInvocation,
|
|
37
|
-
) => Promise<void>;
|
|
38
15
|
/**
|
|
39
16
|
* #969 gate path: plain-language re-ask when prior 给事中 reply was not three-state.
|
|
40
17
|
* Wins over gateReviewInstruction when both present (notary/inspector precedent).
|
|
@@ -60,16 +37,6 @@ export type CountersignRunEnv = PostAdmissionEnv & {
|
|
|
60
37
|
|
|
61
38
|
/** 起居郎 identity outcome — escalate stays distinct from missing terminal. */
|
|
62
39
|
export type CourtDiaristIdentity =
|
|
63
|
-
| {
|
|
64
|
-
readonly kind: "ticket";
|
|
65
|
-
readonly ticketNumber: number;
|
|
66
|
-
/**
|
|
67
|
-
* #871 typed co-review set when the LLM explicitly submitted one.
|
|
68
|
-
* Absent means "no new set this turn" — resume must keep the stored run fact
|
|
69
|
-
* (never silently degrade a multi-ticket set to [main]).
|
|
70
|
-
*/
|
|
71
|
-
readonly courtTicketNumbers?: readonly number[];
|
|
72
|
-
}
|
|
73
40
|
| { readonly kind: "unbound" }
|
|
74
41
|
| {
|
|
75
42
|
readonly kind: "escalate";
|
|
@@ -141,7 +108,7 @@ function courtDiaristEscalateDiagnostic(
|
|
|
141
108
|
})}`;
|
|
142
109
|
}
|
|
143
110
|
|
|
144
|
-
/** Env slice
|
|
111
|
+
/** Env slice for the secretariat's pre-ticket diarist summons. */
|
|
145
112
|
export type CourtDiaristSummonEnv = Pick<
|
|
146
113
|
CountersignRunEnv,
|
|
147
114
|
| "cwd"
|
|
@@ -153,53 +120,6 @@ export type CourtDiaristSummonEnv = Pick<
|
|
|
153
120
|
| "hostAdapters"
|
|
154
121
|
>;
|
|
155
122
|
|
|
156
|
-
/**
|
|
157
|
-
* Read #871 set from preserved diarist payloads.
|
|
158
|
-
* - Field absent / null / non-array → undefined (no new set; resume keeps store).
|
|
159
|
-
* - Explicit [] → single-ticket [main] whole-set replace (signed empty-set contract).
|
|
160
|
-
* - Non-empty array with zero lawful typed members after projection → not a new set
|
|
161
|
-
* (do not impersonate explicit empty and wipe a stored multi-ticket fact).
|
|
162
|
-
* - Non-empty array with ≥1 lawful member → sole type/dedupe projection + principal guarantee.
|
|
163
|
-
* - Multiple submissions: last qualifying set wins.
|
|
164
|
-
* Live path never shape-rejects the role turn; durable damage is a separate seam.
|
|
165
|
-
*/
|
|
166
|
-
export function courtTicketNumbersFromOutcome(
|
|
167
|
-
roleOutcome: TerminalRoleOutcome | undefined,
|
|
168
|
-
principalTicket: number,
|
|
169
|
-
submissions?: readonly unknown[],
|
|
170
|
-
): readonly number[] | undefined {
|
|
171
|
-
if (roleOutcome === undefined) return undefined;
|
|
172
|
-
const payloads = courtDiaristPayloadRows(roleOutcome, submissions);
|
|
173
|
-
let latest: readonly number[] | undefined;
|
|
174
|
-
for (const payload of payloads) {
|
|
175
|
-
if (
|
|
176
|
-
typeof payload !== "object" ||
|
|
177
|
-
payload === null ||
|
|
178
|
-
Array.isArray(payload)
|
|
179
|
-
) {
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
const record = payload as Record<string, unknown>;
|
|
183
|
-
if (!Object.hasOwn(record, "courtTicketNumbers")) continue;
|
|
184
|
-
const raw = record.courtTicketNumbers;
|
|
185
|
-
// Only a real array is a candidate set. Non-array is not explicit empty.
|
|
186
|
-
if (!Array.isArray(raw)) continue;
|
|
187
|
-
// Members only — do not inject principal yet, or all-invalid non-empty becomes [main].
|
|
188
|
-
const members = projectCourtTicketNumbers(raw);
|
|
189
|
-
if (members === null) continue;
|
|
190
|
-
if (members.length === 0) {
|
|
191
|
-
// Literal [] is the intentional principal-only replace; non-empty garbage is not.
|
|
192
|
-
if (raw.length === 0) {
|
|
193
|
-
latest =
|
|
194
|
-
projectCourtTicketNumbers([], { principalTicket }) ?? undefined;
|
|
195
|
-
}
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
latest = projectCourtTicketNumbers(raw, { principalTicket }) ?? undefined;
|
|
199
|
-
}
|
|
200
|
-
return latest;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
123
|
/** Latest payload only. An earlier escalate must not block a later non-escalate submission. */
|
|
204
124
|
export function latestPayloadEscalated(
|
|
205
125
|
roleOutcome: TerminalRoleOutcome | undefined,
|
|
@@ -223,11 +143,8 @@ export function courtDiaristEscalated(
|
|
|
223
143
|
|
|
224
144
|
/**
|
|
225
145
|
* Invoke public 起居郎 under the court-pipeline quiet face.
|
|
226
|
-
* Returns
|
|
227
|
-
* Non-zero exit without escalate status is not rethrown here — caller
|
|
228
|
-
* whether refresh must fail or first-entry settles controlled failure.
|
|
229
|
-
* When `boundTicketNumber` is set (typed handoff from countersign), diarist
|
|
230
|
-
* binds under that key before the turn — never mechanical recognition from prose.
|
|
146
|
+
* Returns escalate or unbound; the secretariat needs no ticket handoff.
|
|
147
|
+
* Non-zero exit without escalate status is not rethrown here — caller settles controlled failure.
|
|
231
148
|
*/
|
|
232
149
|
export async function invokeCourtDiarist(
|
|
233
150
|
input: {
|
|
@@ -237,13 +154,11 @@ export async function invokeCourtDiarist(
|
|
|
237
154
|
readonly attachmentPaths?: readonly string[];
|
|
238
155
|
/** Direct parent run id for durable child lineage (ADR 0010). */
|
|
239
156
|
readonly correlationId?: string;
|
|
240
|
-
/** Already-verified typed key from countersign (refresh / post-assert handoff). */
|
|
241
|
-
readonly boundTicketNumber?: number;
|
|
242
157
|
},
|
|
243
158
|
env: CourtDiaristSummonEnv,
|
|
244
159
|
io: CliIo,
|
|
245
160
|
): Promise<CourtDiaristInvocationResult> {
|
|
246
|
-
// Quiet face: the
|
|
161
|
+
// Quiet face: the secretariat caller must not see diarist CLI chatter.
|
|
247
162
|
const quietIo: CliIo = {
|
|
248
163
|
stdout() {},
|
|
249
164
|
stderr(text: string) {
|
|
@@ -272,9 +187,6 @@ export async function invokeCourtDiarist(
|
|
|
272
187
|
...(input.correlationId === undefined
|
|
273
188
|
? {}
|
|
274
189
|
: { correlationId: input.correlationId }),
|
|
275
|
-
...(input.boundTicketNumber === undefined
|
|
276
|
-
? {}
|
|
277
|
-
: { boundTicketNumber: input.boundTicketNumber }),
|
|
278
190
|
// Child seat selects from the composition-root table. Do not pass the
|
|
279
191
|
// already-selected parent adapter (#840 / ADR 0082: --host 旗标>席位配置>缺省 pi).
|
|
280
192
|
...(env.hostAdapters === undefined
|
|
@@ -311,97 +223,8 @@ export async function invokeCourtDiarist(
|
|
|
311
223
|
};
|
|
312
224
|
}
|
|
313
225
|
|
|
314
|
-
const asserted = (result.admitted as { ticketNumber?: number } | undefined)
|
|
315
|
-
?.ticketNumber;
|
|
316
|
-
if (isSafePositiveTicketNumber(asserted)) {
|
|
317
|
-
const courtTicketNumbers = courtTicketNumbersFromOutcome(
|
|
318
|
-
roleOutcome,
|
|
319
|
-
asserted,
|
|
320
|
-
submissions,
|
|
321
|
-
);
|
|
322
|
-
return {
|
|
323
|
-
identity: {
|
|
324
|
-
kind: "ticket",
|
|
325
|
-
ticketNumber: asserted,
|
|
326
|
-
...(courtTicketNumbers === undefined ? {} : { courtTicketNumbers }),
|
|
327
|
-
},
|
|
328
|
-
...(result.admitted === undefined ? {} : { admitted: result.admitted }),
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
226
|
return {
|
|
332
227
|
identity: { kind: "unbound" },
|
|
333
228
|
...(result.admitted === undefined ? {} : { admitted: result.admitted }),
|
|
334
229
|
};
|
|
335
230
|
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Court-pipeline prior station: refresh this ticket's 起居录 before the
|
|
339
|
-
* countersign body turn when already bound (ADR 0075 每次过庭都跑)。
|
|
340
|
-
* Caller-invisible — no diarist argv on the countersign command line.
|
|
341
|
-
*
|
|
342
|
-
* Missing ticketNumber (true-unbound / identity deferred) skips the refresh
|
|
343
|
-
* station — no diary is minted for a true-unbound run. First-entry identity
|
|
344
|
-
* lives on the shared countersign entry (typed 起居郎 key for bind). Bound refresh:
|
|
345
|
-
* technical failure propagates; LLM escalation remains a recorded conclusion.
|
|
346
|
-
* Path delivery onto materials is not this station's job — post-admission owns it.
|
|
347
|
-
*/
|
|
348
|
-
export async function runCountersignCourtDiaristStation(
|
|
349
|
-
admitted: AdmittedCountersignInvocation,
|
|
350
|
-
env: CountersignRunEnv,
|
|
351
|
-
io: CliIo,
|
|
352
|
-
afterTicketNumber?: number,
|
|
353
|
-
): Promise<{ readonly terminal: import("./terminal.ts").TerminalResult; readonly childRunId?: string } | undefined> {
|
|
354
|
-
if (env.runCourtDiaristStation !== undefined) {
|
|
355
|
-
await env.runCourtDiaristStation(admitted);
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
// Production court refresh (ADR 0075: 每次过庭都跑是调用者用法) only under a known ticket identity.
|
|
359
|
-
// First-entry unbound identity is owned by the shared countersign entry.
|
|
360
|
-
if (admitted.ticketNumber === undefined) return;
|
|
361
|
-
|
|
362
|
-
// #871: refresh every member of the typed co-review set; single-ticket face
|
|
363
|
-
// is just the set [main]. Present-but-empty / missing-principal is damage —
|
|
364
|
-
// never silently fall back to main-only (legacy absent field still may).
|
|
365
|
-
let refreshTickets: readonly number[];
|
|
366
|
-
if (admitted.courtTicketNumbers !== undefined) {
|
|
367
|
-
if (
|
|
368
|
-
admitted.courtTicketNumbers.length === 0 ||
|
|
369
|
-
!admitted.courtTicketNumbers.includes(admitted.ticketNumber)
|
|
370
|
-
) {
|
|
371
|
-
throw new StationChildExhaustedError(
|
|
372
|
-
`court diarist station: courtTicketNumbers is damaged (empty or missing principal #${admitted.ticketNumber})`,
|
|
373
|
-
);
|
|
374
|
-
}
|
|
375
|
-
refreshTickets = admitted.courtTicketNumbers;
|
|
376
|
-
} else {
|
|
377
|
-
refreshTickets = [admitted.ticketNumber];
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const completedIndex = afterTicketNumber === undefined ? -1 : refreshTickets.indexOf(afterTicketNumber);
|
|
381
|
-
for (const ticketNumber of refreshTickets.slice(completedIndex + 1)) {
|
|
382
|
-
const outcome = await invokeCourtDiarist(
|
|
383
|
-
{
|
|
384
|
-
instruction: admitted.instruction,
|
|
385
|
-
projectRoot: admitted.projectRoot,
|
|
386
|
-
failureLabel: `ticket #${ticketNumber}`,
|
|
387
|
-
correlationId: admitted.runId,
|
|
388
|
-
// Refresh holds a typed key — hand it off so identity is bound before turn.
|
|
389
|
-
boundTicketNumber: ticketNumber,
|
|
390
|
-
},
|
|
391
|
-
env,
|
|
392
|
-
io,
|
|
393
|
-
);
|
|
394
|
-
|
|
395
|
-
if (outcome.failedWithoutEscalate !== undefined) {
|
|
396
|
-
throw new StationChildExhaustedError(
|
|
397
|
-
outcome.failedWithoutEscalate.diagnostic,
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
if (outcome.identity.kind === "escalate" && outcome.terminal !== undefined) {
|
|
401
|
-
return {
|
|
402
|
-
terminal: outcome.terminal,
|
|
403
|
-
...(outcome.admitted === undefined ? {} : { childRunId: outcome.admitted.runId }),
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* One public role run: admit → turn request → post-admission → settle.
|
|
3
3
|
* Seat differences are composition-root fields. An omitted reviewer lens
|
|
4
|
-
* starts two ordinary single-axis runs here.
|
|
5
|
-
* identity on this entry; the court diarist station stays in countersign-run.
|
|
4
|
+
* starts two ordinary single-axis runs here.
|
|
6
5
|
*/
|
|
7
6
|
import { dirname, resolve, sep } from "node:path";
|
|
8
7
|
import { AUDITOR_DOSSIER_PROMPT } from "../compliance-transport.ts";
|
|
@@ -39,7 +38,6 @@ import { CliUsageError } from "./cli-errors.ts";
|
|
|
39
38
|
import {
|
|
40
39
|
admitPublicRole,
|
|
41
40
|
bindAdmittedTicketNumber,
|
|
42
|
-
bindCourtTicketNumbersOnAdmitted,
|
|
43
41
|
buildInstructionTransportPrompt,
|
|
44
42
|
materializeCountersignInvocation,
|
|
45
43
|
persistAdmittedSourceRunPath,
|
|
@@ -98,12 +96,11 @@ import {
|
|
|
98
96
|
import {
|
|
99
97
|
invokeCourtDiarist,
|
|
100
98
|
latestPayloadEscalated,
|
|
101
|
-
runCountersignCourtDiaristStation,
|
|
102
99
|
type CountersignRunEnv,
|
|
103
100
|
} from "./countersign-run.ts";
|
|
104
101
|
export type InstructionSeatRunEnv = PostAdmissionEnv & Pick<
|
|
105
102
|
CountersignRunEnv,
|
|
106
|
-
"reviewReask" | "gateReviewInstruction" | "parentRunPath"
|
|
103
|
+
"reviewReask" | "gateReviewInstruction" | "parentRunPath"
|
|
107
104
|
>;
|
|
108
105
|
|
|
109
106
|
type SeatRunResult = {
|
|
@@ -460,8 +457,7 @@ async function runOmittedLensBatch(
|
|
|
460
457
|
|
|
461
458
|
/**
|
|
462
459
|
* Countersign on the shared entry: gate parent resume, deferred materialization,
|
|
463
|
-
*
|
|
464
|
-
* Court refresh stays on runCountersignCourtDiaristStation.
|
|
460
|
+
* then the same one-shot settlement as every other seat.
|
|
465
461
|
*/
|
|
466
462
|
async function runCountersignBody(
|
|
467
463
|
parsed: PublicSeatParse,
|
|
@@ -516,88 +512,14 @@ async function runCountersignBody(
|
|
|
516
512
|
});
|
|
517
513
|
};
|
|
518
514
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
let childDiaristRunId: string | undefined;
|
|
523
|
-
let pausedDiaristTerminal: TerminalResult | undefined;
|
|
524
|
-
|
|
525
|
-
if (gateParentRunPath === undefined && env.runCourtDiaristStation === undefined) {
|
|
526
|
-
let outcome: Awaited<ReturnType<typeof invokeCourtDiarist>>;
|
|
527
|
-
try {
|
|
528
|
-
outcome = await invokeCourtDiarist({
|
|
529
|
-
instruction: parsed.instruction ?? "",
|
|
530
|
-
projectRoot: admitted.projectRoot,
|
|
531
|
-
failureLabel: "unbound summons",
|
|
532
|
-
correlationId: admitted.runId,
|
|
533
|
-
...(env.boundTicketNumber === undefined ? {} : { boundTicketNumber: env.boundTicketNumber }),
|
|
534
|
-
}, env, io);
|
|
535
|
-
} catch (error) {
|
|
536
|
-
await materializeAdmission(
|
|
537
|
-
isSafePositiveTicketNumber(env.boundTicketNumber) ? env.boundTicketNumber : undefined,
|
|
538
|
-
);
|
|
539
|
-
await markRunAdmitted(admitted, env.principalAuthority);
|
|
540
|
-
return await presentControlledFailure(admitted, {
|
|
541
|
-
timedOut: false,
|
|
542
|
-
code: null,
|
|
543
|
-
stderr: "",
|
|
544
|
-
thrown: error,
|
|
545
|
-
}, seatAdapters(admitted, env), env.principalAuthority, io) as SeatRunResult;
|
|
546
|
-
}
|
|
547
|
-
identityDiaristRan = true;
|
|
548
|
-
if (outcome.identity.kind === "escalate") pausedDiaristTerminal = outcome.terminal;
|
|
549
|
-
if (outcome.admitted?.runDirectory.includes(`${sep}unbound${sep}runs${sep}`)) {
|
|
550
|
-
childDiaristRunId = outcome.admitted.runId;
|
|
551
|
-
}
|
|
552
|
-
if (outcome.failedWithoutEscalate !== undefined) {
|
|
553
|
-
const diagnostic = outcome.failedWithoutEscalate.diagnostic;
|
|
554
|
-
await materializeAdmission(
|
|
555
|
-
isSafePositiveTicketNumber(env.boundTicketNumber) ? env.boundTicketNumber : undefined,
|
|
556
|
-
);
|
|
557
|
-
if (childDiaristRunId !== undefined) await recordChildDiaristRun(admitted, childDiaristRunId);
|
|
558
|
-
await markRunAdmitted(admitted, env.principalAuthority);
|
|
559
|
-
return await presentControlledFailure(admitted, {
|
|
560
|
-
timedOut: false,
|
|
561
|
-
code: null,
|
|
562
|
-
stderr: "",
|
|
563
|
-
thrown: new Error(diagnostic),
|
|
564
|
-
}, seatAdapters(admitted, env), env.principalAuthority, io) as SeatRunResult;
|
|
565
|
-
}
|
|
566
|
-
if (outcome.identity.kind === "ticket") {
|
|
567
|
-
typedTicket = outcome.identity.ticketNumber;
|
|
568
|
-
typedCourtTicketNumbers = outcome.identity.courtTicketNumbers;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
await materializeAdmission(typedTicket);
|
|
515
|
+
await materializeAdmission(
|
|
516
|
+
isSafePositiveTicketNumber(env.boundTicketNumber) ? env.boundTicketNumber : undefined,
|
|
517
|
+
);
|
|
573
518
|
await markRunAdmitted(admitted, env.principalAuthority);
|
|
574
|
-
if (childDiaristRunId !== undefined) {
|
|
575
|
-
await recordChildDiaristRun(admitted, childDiaristRunId);
|
|
576
|
-
}
|
|
577
|
-
if (pausedDiaristTerminal !== undefined) {
|
|
578
|
-
// The child already submitted its own pause; no parent turn exists yet.
|
|
579
|
-
io.stdout(formatTerminalResult(pausedDiaristTerminal));
|
|
580
|
-
return { exitCode: 0, admitted, terminal: pausedDiaristTerminal };
|
|
581
|
-
}
|
|
582
519
|
if (gateParentRunPath !== undefined) {
|
|
583
520
|
await persistAdmittedSourceRunPath(admitted, gateParentRunPath);
|
|
584
521
|
admitted = { ...admitted, sourceRunPath: gateParentRunPath };
|
|
585
522
|
}
|
|
586
|
-
if (identityDiaristRan && typedTicket !== undefined) {
|
|
587
|
-
try {
|
|
588
|
-
await bindAdmittedTicketNumber(admitted, typedTicket);
|
|
589
|
-
await relocateAdmittedRunToTicket(admitted, env.principalAuthority);
|
|
590
|
-
await bindCourtTicketNumbersOnAdmitted(admitted, typedCourtTicketNumbers ?? [typedTicket]);
|
|
591
|
-
} catch (error) {
|
|
592
|
-
return await presentControlledFailure(admitted, {
|
|
593
|
-
timedOut: false,
|
|
594
|
-
code: null,
|
|
595
|
-
stderr: "",
|
|
596
|
-
thrown: error,
|
|
597
|
-
}, seatAdapters(admitted, env), env.principalAuthority, io) as SeatRunResult;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
523
|
const turnProjection: RoleTurnRequestProjectionOptions = {
|
|
602
524
|
packageRoot: env.packageRoot,
|
|
603
525
|
home: env.home,
|
|
@@ -630,13 +552,6 @@ async function runCountersignBody(
|
|
|
630
552
|
...seatAdapters(admitted, env),
|
|
631
553
|
beforeDispatch: async (admittedSeat, lease) => {
|
|
632
554
|
if (!packagedAdmitsCountersign(admittedSeat.role)) return;
|
|
633
|
-
if (!identityDiaristRan || typedTicket !== undefined) {
|
|
634
|
-
const refresh = await runCountersignCourtDiaristStation(admittedSeat, env, io);
|
|
635
|
-
if (refresh !== undefined) {
|
|
636
|
-
io.stdout(formatTerminalResult(refresh.terminal));
|
|
637
|
-
return refresh.terminal;
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
555
|
await relocateAdmittedRunToTicket(admittedSeat, env.principalAuthority, lease);
|
|
641
556
|
Object.assign(turnRequest, buildInstructionSeatTurnRequest(admittedSeat, turnProjection));
|
|
642
557
|
},
|
|
@@ -1019,16 +934,6 @@ export async function continueParentAfterChild(
|
|
|
1019
934
|
return runPublicInstructionSeatResume({ runId: parentRunId }, env, io);
|
|
1020
935
|
}
|
|
1021
936
|
const admitted = loaded.admitted;
|
|
1022
|
-
// Court diarist refresh advances only after a diarist child; gate officers
|
|
1023
|
-
// (notary/auditor/…) resume straight into queueConclusionFromChild (#1057).
|
|
1024
|
-
if (admitted.role === "countersign" && admitted.ticketNumber !== undefined
|
|
1025
|
-
&& child.role === "diarist") {
|
|
1026
|
-
const refresh = await runCountersignCourtDiaristStation(admitted, env, io, child.ticketNumber);
|
|
1027
|
-
if (refresh !== undefined) {
|
|
1028
|
-
io.stdout(formatTerminalResult(refresh.terminal));
|
|
1029
|
-
return { exitCode: 0, admitted, terminal: refresh.terminal };
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
937
|
const resolved = await queueConclusionFromChild(child, env, io);
|
|
1033
938
|
if (resolved !== undefined && "stop" in resolved) return resolved.stop;
|
|
1034
939
|
if (resolved !== undefined && resolved.status === "escalate") {
|
|
@@ -60,7 +60,6 @@ import {
|
|
|
60
60
|
import {
|
|
61
61
|
loadDoctorCase,
|
|
62
62
|
} from "../doctor-evidence.ts";
|
|
63
|
-
import { projectCourtTicketNumbers } from "../diarist-contracts.ts";
|
|
64
63
|
import type { DoctorCaseIdentity } from "../doctor-contracts.ts";
|
|
65
64
|
import {
|
|
66
65
|
emptyCollectorManifest,
|
|
@@ -156,17 +155,6 @@ export type AdmittedJudgeInvocation = AdmittedRoleInvocationBase & {
|
|
|
156
155
|
|
|
157
156
|
export type AdmittedCountersignInvocation = AdmittedRoleInvocationBase & {
|
|
158
157
|
readonly role: "countersign";
|
|
159
|
-
/**
|
|
160
|
-
* #871 typed co-review set for this countersign run (durable run fact).
|
|
161
|
-
* Main ticket remains `ticketNumber`; this set drives per-ticket court diarist refresh.
|
|
162
|
-
* Whole-set replace on new typed submission — never union with history.
|
|
163
|
-
*/
|
|
164
|
-
courtTicketNumbers?: readonly number[];
|
|
165
|
-
/**
|
|
166
|
-
* Load-time durable set damage diagnostic (#871 B7). Resume must settle as
|
|
167
|
-
* countersign controlled failure with this text — never structural exit 2 or main-only.
|
|
168
|
-
*/
|
|
169
|
-
courtTicketNumbersDamage?: string;
|
|
170
158
|
/**
|
|
171
159
|
* Parent run directory (#747 / #987 gate same-parent resume). Persisted on first
|
|
172
160
|
* mint when gate supplies parentRunPath; independent of ticket-number lookup.
|
|
@@ -338,7 +326,7 @@ export function issueAdmissionPlacement(
|
|
|
338
326
|
readonly cwd: string;
|
|
339
327
|
readonly runId: string;
|
|
340
328
|
readonly role: AdmittedRoleInvocation["role"];
|
|
341
|
-
/**
|
|
329
|
+
/** Typed identity, when supplied; never derive it from CLI prose. */
|
|
342
330
|
readonly subject: RoleRunSubject;
|
|
343
331
|
readonly home?: string;
|
|
344
332
|
/** Placement may be computed before a same-ticket lookup without touching disk. */
|
|
@@ -594,56 +582,6 @@ export async function recordAdmittedCorrelation(
|
|
|
594
582
|
mutable.correlationIds = correlationIds;
|
|
595
583
|
}
|
|
596
584
|
|
|
597
|
-
/**
|
|
598
|
-
* #871: persist the typed co-review set as a countersign run fact (admitted-request +
|
|
599
|
-
* invocation.json). Whole-set replace — never union with a prior set. Main ticket
|
|
600
|
-
* binding stays on `ticketNumber` alone. Projection reuses the sole contract helper;
|
|
601
|
-
* principal membership is mandatory.
|
|
602
|
-
*/
|
|
603
|
-
export async function bindCourtTicketNumbersOnAdmitted(
|
|
604
|
-
admitted: AdmittedCountersignInvocation,
|
|
605
|
-
courtTicketNumbers: readonly number[],
|
|
606
|
-
): Promise<void> {
|
|
607
|
-
if (admitted.ticketNumber === undefined) {
|
|
608
|
-
throw new Error(
|
|
609
|
-
"bindCourtTicketNumbersOnAdmitted requires a bound principal ticketNumber",
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
const principal = requireSafePositiveTicketNumber(
|
|
613
|
-
admitted.ticketNumber,
|
|
614
|
-
"bindCourtTicketNumbersOnAdmitted principal ticketNumber",
|
|
615
|
-
);
|
|
616
|
-
// Strict write path: every member must already be a lawful number (no soft filter).
|
|
617
|
-
for (const item of courtTicketNumbers) {
|
|
618
|
-
if (!isSafePositiveTicketNumber(item)) {
|
|
619
|
-
throw new Error(
|
|
620
|
-
`bindCourtTicketNumbersOnAdmitted requires safe positive integers, got ${String(item)}`,
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
const projected = projectCourtTicketNumbers(courtTicketNumbers, {
|
|
625
|
-
principalTicket: principal,
|
|
626
|
-
});
|
|
627
|
-
if (projected === null || projected.length === 0) {
|
|
628
|
-
throw new Error("bindCourtTicketNumbersOnAdmitted requires a non-empty ticket set");
|
|
629
|
-
}
|
|
630
|
-
const frozen = Object.freeze([...projected]);
|
|
631
|
-
const admittedPath = admitted.admittedRequestPath;
|
|
632
|
-
const current = JSON.parse(await readFile(admittedPath, "utf8")) as Record<
|
|
633
|
-
string,
|
|
634
|
-
unknown
|
|
635
|
-
>;
|
|
636
|
-
await writeFile(
|
|
637
|
-
admittedPath,
|
|
638
|
-
`${JSON.stringify({ ...current, courtTicketNumbers: frozen }, null, 2)}\n`,
|
|
639
|
-
"utf8",
|
|
640
|
-
);
|
|
641
|
-
await mergeInvocationIdentityPage(admitted.runDirectory, {
|
|
642
|
-
courtTicketNumbers: frozen,
|
|
643
|
-
});
|
|
644
|
-
admitted.courtTicketNumbers = frozen;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
585
|
/** File a role run under its latest typed ticket identity. */
|
|
648
586
|
export async function relocateAdmittedRunToTicket(
|
|
649
587
|
admitted: AdmittedRoleInvocation,
|
|
@@ -1904,7 +1842,7 @@ export type AdmitJudgeInvocationOptions = {
|
|
|
1904
1842
|
|
|
1905
1843
|
export type AdmitInspectorInvocationOptions = AdmitJudgeInvocationOptions & {
|
|
1906
1844
|
correlationId?: string;
|
|
1907
|
-
/** Typed identity
|
|
1845
|
+
/** Typed identity from the caller or an already-bound run. */
|
|
1908
1846
|
assertedTicketNumber?: number;
|
|
1909
1847
|
};
|
|
1910
1848
|
|
|
@@ -211,15 +211,6 @@ async function settleProcessCancelAfterTurn<A extends AdmittedRoleInvocation, T
|
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
/**
|
|
215
|
-
* Nested station-child role already finished its own call-local loop.
|
|
216
|
-
* Parent records that failure once and must not auto-resume into a re-summon
|
|
217
|
-
* (#840 父子不层叠). Other beforeDispatch failures keep the shared #416 budget.
|
|
218
|
-
*/
|
|
219
|
-
export class StationChildExhaustedError extends Error {
|
|
220
|
-
override readonly name = "StationChildExhaustedError";
|
|
221
|
-
}
|
|
222
|
-
|
|
223
214
|
function withOnceSuccessfulBeforeDispatch<
|
|
224
215
|
A extends AdmittedRoleInvocation,
|
|
225
216
|
T extends TerminalResult = TerminalResult,
|
|
@@ -825,16 +816,12 @@ export async function dispatchPostAdmissionTurn<
|
|
|
825
816
|
io,
|
|
826
817
|
persistRunState,
|
|
827
818
|
)) as { exitCode: number; admitted: A; terminal: T };
|
|
828
|
-
if (error instanceof StationChildExhaustedError) {
|
|
829
|
-
return { ...settled, skipAutoResume: true as const, ...deferredPersist };
|
|
830
|
-
}
|
|
831
819
|
return { ...settled, ...deferredPersist };
|
|
832
820
|
}
|
|
833
821
|
}
|
|
834
822
|
|
|
835
|
-
// Turn request is assembled after beforeDispatch so this turn sees
|
|
836
|
-
//
|
|
837
|
-
// writes (#742). Dialogue continuation stays caller/peer opaque. #1092: no
|
|
823
|
+
// Turn request is assembled after beforeDispatch so this turn sees any
|
|
824
|
+
// seat-specific admission changes. Dialogue continuation stays caller/peer opaque. #1092: no
|
|
838
825
|
// code-side 起居录 path freeze or readingMaterial inject — roles read by ticket.
|
|
839
826
|
// No package-resume parallel face or typed resume identity.
|
|
840
827
|
// #990: one-shot / auto-resume may freeze principal+runDirectory before
|