@akagilnc/pi-workflow-roles 0.1.5756 → 0.1.5775
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 +132 -79
- package/dist/headless-host/production-host.js +132 -79
- package/dist/migrate-book-topology.js +7 -6
- package/dist/navigator-attendance.js +1 -1
- package/dist/packaged-role-registry.js +3 -2
- package/dist/public-cli/instruction-seat-run.js +144 -55
- package/dist/public-cli/invocation.js +3 -7
- package/dist/public-cli/main.js +127 -77
- package/dist/public-cli/option-definitions.js +4 -4
- package/dist/public-cli/post-admission.js +17 -28
- package/dist/public-cli/run-lifecycle.js +0 -17
- package/dist/public-role-summons.js +11 -8
- package/dist/run-terminal-artifacts.js +0 -7
- package/package.json +5 -5
- package/src/navigator-attendance.ts +2 -1
- package/src/packaged-role-registry.ts +3 -2
- package/src/public-cli/instruction-seat-run.ts +158 -63
- package/src/public-cli/invocation.ts +4 -8
- package/src/public-cli/option-definitions.ts +5 -4
- package/src/public-cli/post-admission.ts +16 -29
- package/src/public-cli/run-lifecycle.ts +0 -19
- package/src/public-role-summons.ts +27 -16
- package/src/role-runtime.ts +7 -1
- package/src/run-terminal-artifacts.ts +0 -7
|
@@ -99,10 +99,3 @@ export function runIdFromRunDirectory(runDirectory) {
|
|
|
99
99
|
export function roleFromRunDirectory(runDirectory) {
|
|
100
100
|
return parseRunLeaf(basename(runDirectory))?.role;
|
|
101
101
|
}
|
|
102
|
-
/**
|
|
103
|
-
* #1195 ticket-court blind review: notary source is a countersign run.
|
|
104
|
-
* Identity still binds; countersign body is not preloaded into dialogue.
|
|
105
|
-
*/
|
|
106
|
-
export function isTicketCourtCountersignSource(runDirectory) {
|
|
107
|
-
return roleFromRunDirectory(runDirectory) === "countersign";
|
|
108
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akagilnc/pi-workflow-roles",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5775",
|
|
4
4
|
"description": "Soul-bound workflow roles for Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.build.json && node scripts/build-package.mjs",
|
|
46
46
|
"prepack": "npm run build",
|
|
47
|
-
"test": "node --import tsx --import ./scripts/test-process-env-preload.mjs --test test/unit/**/*.test.ts test/contract/**/*.test.ts",
|
|
48
|
-
"test:fast": "node --import tsx --import ./scripts/test-process-env-preload.mjs --test-timeout=300000 --test test/unit/**/*.test.ts test/contract/**/*.test.ts",
|
|
49
|
-
"test:integration": "node --import tsx --import ./scripts/test-process-env-preload.mjs --test test/unit/**/*.test.ts test/contract/**/*.test.ts test/integration/**/*.test.ts",
|
|
47
|
+
"test": "node --import tsx --import ./scripts/test-process-env-preload.mjs --test-reporter=dot --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=node-test-report.txt --test test/unit/**/*.test.ts test/contract/**/*.test.ts",
|
|
48
|
+
"test:fast": "node --import tsx --import ./scripts/test-process-env-preload.mjs --test-timeout=300000 --test-reporter=dot --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=node-test-report.txt --test test/unit/**/*.test.ts test/contract/**/*.test.ts",
|
|
49
|
+
"test:integration": "node --import tsx --import ./scripts/test-process-env-preload.mjs --test-reporter=dot --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=node-test-report.txt --test test/unit/**/*.test.ts test/contract/**/*.test.ts test/integration/**/*.test.ts",
|
|
50
50
|
"test:all": "node scripts/run-test-all.mjs",
|
|
51
|
-
"test:adjudication": "node --import tsx --import ./scripts/test-process-env-preload.mjs --test test/adjudication/**/*.test.ts",
|
|
51
|
+
"test:adjudication": "node --import tsx --import ./scripts/test-process-env-preload.mjs --test-reporter=dot --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=node-test-report.txt --test test/adjudication/**/*.test.ts",
|
|
52
52
|
"typecheck": "tsc --noEmit",
|
|
53
53
|
"migrate:book-topology": "node dist/migrate-book-topology.js"
|
|
54
54
|
},
|
|
@@ -444,9 +444,10 @@ export function createNavigatorAttendance(options: NavigatorAttendanceOptions) {
|
|
|
444
444
|
};
|
|
445
445
|
await promptAllowingRejectedPrepare(request);
|
|
446
446
|
// Correction after rejected prepare on the sole model round (#1160).
|
|
447
|
+
// #1208: resend this turn's prepare request; delivery-state stays on policy facts.
|
|
447
448
|
while (output === undefined && prepareBatchRejected && delivery.nextAction() === "request-delivery") {
|
|
448
449
|
delivery.recordDeliveryRequest();
|
|
449
|
-
await promptAllowingRejectedPrepare(
|
|
450
|
+
await promptAllowingRejectedPrepare(request);
|
|
450
451
|
}
|
|
451
452
|
if (output === undefined && delivery.nextAction() === "request-delivery") {
|
|
452
453
|
delivery.closeBudget();
|
|
@@ -294,9 +294,8 @@ export const PUBLIC_ROLE_RECORDS = [
|
|
|
294
294
|
bareCommand: false,
|
|
295
295
|
outputTool: NOTARY_OUTPUT_TOOL_NAME,
|
|
296
296
|
settlement: "accepted",
|
|
297
|
-
/**
|
|
297
|
+
/** Dialogue via shared resolveReviewSeatDialogueBody (review = fixed dispatch). */
|
|
298
298
|
reaskPrompt: true,
|
|
299
|
-
transportPrompt: "fixed-kickoff",
|
|
300
299
|
gateStageLabel: "符宝郎",
|
|
301
300
|
/** Gate summon binds --source-run and keeps dialogue off the argv. */
|
|
302
301
|
gateSummon: "source-run",
|
|
@@ -319,6 +318,8 @@ export const PUBLIC_ROLE_RECORDS = [
|
|
|
319
318
|
phases: [null],
|
|
320
319
|
outputTool: COUNTERSIGN_OUTPUT_TOOL_NAME,
|
|
321
320
|
settlement: "accepted",
|
|
321
|
+
/** Same shared dialogue resolver as other review seats (#1208 C1). */
|
|
322
|
+
reaskPrompt: true,
|
|
322
323
|
gateStageLabel: "给事中",
|
|
323
324
|
/** Gate summon carries the parent payload as the instruction and a parent run id. */
|
|
324
325
|
gateSummon: "parent-instruction",
|
|
@@ -75,7 +75,6 @@ import {
|
|
|
75
75
|
} from "./post-admission.ts";
|
|
76
76
|
import { presentTerminal } from "./auto-resume.ts";
|
|
77
77
|
import {
|
|
78
|
-
buildAutoResumeContinuationPrompt,
|
|
79
78
|
loadResumablePublicRole,
|
|
80
79
|
markRunAdmitted,
|
|
81
80
|
readCurrentCourt,
|
|
@@ -138,6 +137,20 @@ const POST_SUBMISSION_ROUTING: Partial<Record<PackagedRole, {
|
|
|
138
137
|
judge: {
|
|
139
138
|
statuses: REVIEW_QUEUE_STATUSES,
|
|
140
139
|
},
|
|
140
|
+
// #1208 C1 / ADR 0055: every review-queue seat uses the shared
|
|
141
|
+
// unreadable-status seam (officerConclusionReask). Independent public
|
|
142
|
+
// entry for notary/auditor/inspector was the same miss — route here, do
|
|
143
|
+
// not invent a parallel loop. Gate summons still own the outer budget
|
|
144
|
+
// via nestedIncomplete when this seat exhausts unsettled.
|
|
145
|
+
notary: {
|
|
146
|
+
statuses: REVIEW_QUEUE_STATUSES,
|
|
147
|
+
},
|
|
148
|
+
auditor: {
|
|
149
|
+
statuses: REVIEW_QUEUE_STATUSES,
|
|
150
|
+
},
|
|
151
|
+
inspector: {
|
|
152
|
+
statuses: REVIEW_QUEUE_STATUSES,
|
|
153
|
+
},
|
|
141
154
|
coder: {
|
|
142
155
|
statuses: WORKER_ROUTING_STATUSES,
|
|
143
156
|
reask: WORKER_STATUS_REASK,
|
|
@@ -271,11 +284,14 @@ async function reaskUnreadablePostSubmissionStatus(
|
|
|
271
284
|
if (message === undefined) return undefined;
|
|
272
285
|
const next = withUnreadableReask(env);
|
|
273
286
|
if (next === undefined) return UNREADABLE_REASK_EXHAUSTED;
|
|
287
|
+
// Named status-reask: summons opens a new court; message carries the diagnostic
|
|
288
|
+
// (not arbitrary summons.instruction passthrough through the review resolver).
|
|
274
289
|
return runPublicInstructionSeatResume(
|
|
275
290
|
{
|
|
276
291
|
runId: admitted.runId,
|
|
277
292
|
runDirectory: admitted.runDirectory,
|
|
278
|
-
|
|
293
|
+
message,
|
|
294
|
+
summons: { instructionEmpty: true },
|
|
279
295
|
},
|
|
280
296
|
next,
|
|
281
297
|
io,
|
|
@@ -329,11 +345,14 @@ async function reaskMissingTicketOnce(
|
|
|
329
345
|
softTicketReaskTurn: true,
|
|
330
346
|
softTicketReaskOwnership,
|
|
331
347
|
};
|
|
348
|
+
// Named #1171 missing-ticket: summons opens a new court; message carries the
|
|
349
|
+
// package reask copy (not arbitrary summons.instruction passthrough).
|
|
332
350
|
const result = await runPublicInstructionSeatResume(
|
|
333
351
|
{
|
|
334
352
|
runId: admitted.runId,
|
|
335
353
|
runDirectory: admitted.runDirectory,
|
|
336
|
-
|
|
354
|
+
message: instruction,
|
|
355
|
+
summons: { instructionEmpty: true },
|
|
337
356
|
},
|
|
338
357
|
softReaskEnv,
|
|
339
358
|
io,
|
|
@@ -505,8 +524,8 @@ export function buildInstructionSeatTurnRequest(
|
|
|
505
524
|
|
|
506
525
|
/**
|
|
507
526
|
* Direct review-seat call: reconnect ledger peer-body delivery (ADR 0085 / #1166).
|
|
508
|
-
* Gate summons already ride gateReviewInstruction; direct --source-run
|
|
509
|
-
*
|
|
527
|
+
* Gate summons already ride gateReviewInstruction; direct --source-run falls
|
|
528
|
+
* through the shared dialogue resolver (notary: fixed dispatch).
|
|
510
529
|
*/
|
|
511
530
|
async function loadLedgerPeerBody(
|
|
512
531
|
sourceRunDirectory: string,
|
|
@@ -529,34 +548,40 @@ async function loadLedgerPeerBody(
|
|
|
529
548
|
return readableGateItem(latest.accepted);
|
|
530
549
|
}
|
|
531
550
|
|
|
532
|
-
/** Owner-approved fixed Notary dispatch (2026-10-09); never the parent verdict. */
|
|
551
|
+
/** Owner-approved fixed Notary review dispatch (2026-10-09 #1208); never the parent verdict. */
|
|
533
552
|
export const NOTARY_FIXED_DISPATCH = "请审本轮受审对象,按符宝郎职掌交卷。";
|
|
534
553
|
|
|
535
554
|
/**
|
|
536
555
|
* One delivery rule for review-seat dialogue on new turns and same-parent resume
|
|
537
|
-
* (#1166 / ADR 0085 / ADR 0087):
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
556
|
+
* (#1166 / ADR 0085 / ADR 0087 / #1208): notary review uses fixed dispatch;
|
|
557
|
+
* named operational reasks ride reviewReask (first-mint / gate) or the resume
|
|
558
|
+
* request message seam — never arbitrary non-empty caller/summons passthrough.
|
|
559
|
+
* Other seats prefer format reask, then gate body, then caller dispatch, then
|
|
560
|
+
* ledger peer. Identity stays in startup materials. Notary never takes
|
|
561
|
+
* gate/parent verdict as dialogue (gateReviewInstruction stays unread for notary).
|
|
541
562
|
*/
|
|
542
|
-
async function resolveReviewSeatDialogueBody(input: {
|
|
563
|
+
export async function resolveReviewSeatDialogueBody(input: {
|
|
543
564
|
readonly role?: string;
|
|
544
|
-
readonly
|
|
565
|
+
readonly reviewReask?: string;
|
|
566
|
+
readonly gateReviewInstruction?: string;
|
|
545
567
|
readonly callerInstruction?: string;
|
|
546
568
|
readonly sourceRunPath?: string;
|
|
547
569
|
readonly projectRoot: string;
|
|
548
570
|
readonly home: string;
|
|
549
571
|
}): Promise<string | undefined> {
|
|
550
|
-
|
|
551
|
-
|
|
572
|
+
// #1208: fixed sentence is the review function only (owner 37bfe80d /
|
|
573
|
+
// 393daf98). reviewReask is the named operational override; caller/summons
|
|
574
|
+
// body is not a free passthrough (countersign rejected that prescription).
|
|
575
|
+
if (input.role === "notary") {
|
|
576
|
+
if (input.reviewReask !== undefined) return input.reviewReask;
|
|
577
|
+
return NOTARY_FIXED_DISPATCH;
|
|
578
|
+
}
|
|
579
|
+
const preferred = input.reviewReask ?? input.gateReviewInstruction;
|
|
580
|
+
if (preferred !== undefined && preferred.length > 0) return preferred;
|
|
552
581
|
const caller = input.callerInstruction ?? "";
|
|
553
582
|
if (caller.length > 0) return caller;
|
|
554
583
|
const sourceRunPath = admittedSourceRunPath(input)?.trim();
|
|
555
584
|
if (sourceRunPath === undefined) return undefined;
|
|
556
|
-
if (input.role === "notary") {
|
|
557
|
-
const { isTicketCourtCountersignSource } = await import("../run-terminal-artifacts.ts");
|
|
558
|
-
if (isTicketCourtCountersignSource(sourceRunPath)) return NOTARY_FIXED_DISPATCH;
|
|
559
|
-
}
|
|
560
585
|
return await loadLedgerPeerBody(sourceRunPath, input.projectRoot, input.home);
|
|
561
586
|
}
|
|
562
587
|
|
|
@@ -567,10 +592,12 @@ async function resolveInitialPrompt(
|
|
|
567
592
|
const record = roleRecord(admitted.role);
|
|
568
593
|
if ("reaskPrompt" in record && record.reaskPrompt === true) {
|
|
569
594
|
const sourceRunPath = admittedSourceRunPath(admitted);
|
|
570
|
-
const reask = env.reviewReask ?? env.gateReviewInstruction;
|
|
571
595
|
const body = await resolveReviewSeatDialogueBody({
|
|
572
596
|
role: admitted.role,
|
|
573
|
-
...(
|
|
597
|
+
...(env.reviewReask === undefined ? {} : { reviewReask: env.reviewReask }),
|
|
598
|
+
...(env.gateReviewInstruction === undefined
|
|
599
|
+
? {}
|
|
600
|
+
: { gateReviewInstruction: env.gateReviewInstruction }),
|
|
574
601
|
callerInstruction: admitted.instruction,
|
|
575
602
|
...(sourceRunPath === undefined ? {} : { sourceRunPath }),
|
|
576
603
|
projectRoot: admitted.projectRoot,
|
|
@@ -702,6 +729,7 @@ async function dispatchAdmitted(
|
|
|
702
729
|
const initialPromptText = await resolveInitialPrompt(admitted, activeEnv);
|
|
703
730
|
const auto = "inCallAutoResume" in record && record.inCallAutoResume === true;
|
|
704
731
|
if (auto) {
|
|
732
|
+
// #1208: auto-resume resends this turn's actual content, never a filler phrase.
|
|
705
733
|
return await runPostAdmissionResumable({
|
|
706
734
|
admitted,
|
|
707
735
|
env: activeEnv,
|
|
@@ -717,7 +745,7 @@ async function dispatchAdmitted(
|
|
|
717
745
|
admitted,
|
|
718
746
|
roleTurnOptions(activeEnv, admitted, {
|
|
719
747
|
kind: "resume",
|
|
720
|
-
prompt:
|
|
748
|
+
prompt: initialPromptText,
|
|
721
749
|
}),
|
|
722
750
|
),
|
|
723
751
|
adapters,
|
|
@@ -851,21 +879,28 @@ function resumeSameParentInstructionSeat(input: {
|
|
|
851
879
|
...(input.ticketNumber === undefined ? {} : { ticketNumber: input.ticketNumber }),
|
|
852
880
|
freshSummons: input.env.freshSummons,
|
|
853
881
|
summons: input.summons,
|
|
854
|
-
resume: (runId, materials, runDirectory) =>
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
882
|
+
resume: (runId, materials, runDirectory) => {
|
|
883
|
+
// Named operational reask on this summon: explicit message seam.
|
|
884
|
+
// Review dialogue stays on summons → shared resolver (notary: fixed).
|
|
885
|
+
// Do not stuff reask into summons.instruction for later passthrough.
|
|
886
|
+
const reask = input.env.reviewReask;
|
|
887
|
+
return runPublicInstructionSeatResume(
|
|
888
|
+
{
|
|
889
|
+
runId,
|
|
890
|
+
runDirectory,
|
|
891
|
+
...(reask === undefined ? {} : { message: reask }),
|
|
892
|
+
...(materials === undefined ? {} : { summons: materials }),
|
|
893
|
+
},
|
|
894
|
+
input.env,
|
|
895
|
+
input.io,
|
|
896
|
+
);
|
|
897
|
+
},
|
|
863
898
|
});
|
|
864
899
|
}
|
|
865
900
|
|
|
866
901
|
/**
|
|
867
902
|
* Same delivery rule as resolveInitialPrompt for same-parent resume summons.
|
|
868
|
-
*
|
|
903
|
+
* Shared resolver owns the input choice — callers only pass env fields.
|
|
869
904
|
*/
|
|
870
905
|
async function sameParentDialogue(
|
|
871
906
|
env: InstructionSeatRunEnv,
|
|
@@ -874,10 +909,12 @@ async function sameParentDialogue(
|
|
|
874
909
|
projectRoot: string,
|
|
875
910
|
role: string,
|
|
876
911
|
): Promise<{ readonly instruction: string; readonly instructionEmpty: boolean }> {
|
|
877
|
-
const reask = env.reviewReask ?? env.gateReviewInstruction;
|
|
878
912
|
const body = await resolveReviewSeatDialogueBody({
|
|
879
913
|
role,
|
|
880
|
-
...(
|
|
914
|
+
...(env.reviewReask === undefined ? {} : { reviewReask: env.reviewReask }),
|
|
915
|
+
...(env.gateReviewInstruction === undefined
|
|
916
|
+
? {}
|
|
917
|
+
: { gateReviewInstruction: env.gateReviewInstruction }),
|
|
881
918
|
callerInstruction: fallback.instruction,
|
|
882
919
|
sourceRunPath,
|
|
883
920
|
projectRoot,
|
|
@@ -898,16 +935,27 @@ async function runCountersignBody(
|
|
|
898
935
|
env: InstructionSeatRunEnv,
|
|
899
936
|
io: CliIo,
|
|
900
937
|
): Promise<SeatRunResult> {
|
|
938
|
+
const projectRoot = resolve(parsed.project ?? env.cwd);
|
|
901
939
|
const gateParentRunPath =
|
|
902
940
|
typeof env.parentRunPath === "string" && env.parentRunPath.trim() !== ""
|
|
903
941
|
? env.parentRunPath
|
|
904
942
|
: undefined;
|
|
905
943
|
if (gateParentRunPath !== undefined) {
|
|
906
|
-
|
|
944
|
+
// #1208 C1: same shared dialogue resolver as every other same-parent seat.
|
|
945
|
+
const dialogue = await sameParentDialogue(
|
|
946
|
+
env,
|
|
947
|
+
{
|
|
948
|
+
instruction: parsed.instruction ?? "",
|
|
949
|
+
instructionEmpty: (parsed.instruction ?? "").length === 0,
|
|
950
|
+
},
|
|
951
|
+
gateParentRunPath,
|
|
952
|
+
projectRoot,
|
|
953
|
+
"countersign",
|
|
954
|
+
);
|
|
907
955
|
const resumed = await resumeSameParentInstructionSeat({
|
|
908
956
|
env,
|
|
909
957
|
io,
|
|
910
|
-
projectRoot
|
|
958
|
+
projectRoot,
|
|
911
959
|
role: "countersign",
|
|
912
960
|
parentRunPath: gateParentRunPath,
|
|
913
961
|
...(isSafePositiveTicketNumber(env.boundTicketNumber)
|
|
@@ -915,8 +963,8 @@ async function runCountersignBody(
|
|
|
915
963
|
: {}),
|
|
916
964
|
summons: {
|
|
917
965
|
sourceRunPath: gateParentRunPath,
|
|
918
|
-
instruction:
|
|
919
|
-
instructionEmpty:
|
|
966
|
+
instruction: dialogue.instruction,
|
|
967
|
+
instructionEmpty: dialogue.instructionEmpty,
|
|
920
968
|
// #1166 J11: same-parent resume delivers this call's file-flag paths
|
|
921
969
|
// through the shared summons → appendCallerFileFlagPaths rule.
|
|
922
970
|
attachmentPaths: parsed.attachmentPaths ?? [],
|
|
@@ -954,14 +1002,10 @@ async function runCountersignBody(
|
|
|
954
1002
|
await persistAdmittedSourceRunPath(admitted, gateParentRunPath);
|
|
955
1003
|
admitted = { ...admitted, sourceRunPath: gateParentRunPath };
|
|
956
1004
|
}
|
|
957
|
-
// #1166 J11:
|
|
958
|
-
// resolveInitialPrompt / resumeTurnRequestProjectionOptions — never body alone.
|
|
959
|
-
const reviewBody = env.reviewReask ?? env.gateReviewInstruction;
|
|
1005
|
+
// #1208 C1 / #1166 J11: one shared initial-prompt seam (incl. file flags).
|
|
960
1006
|
const turnProjection = roleTurnOptions(env, admitted, {
|
|
961
1007
|
kind: "initial",
|
|
962
|
-
prompt:
|
|
963
|
-
? appendCallerFileFlagPaths(reviewBody, admitted.attachments)
|
|
964
|
-
: buildInstructionTransportPrompt(admitted),
|
|
1008
|
+
prompt: await resolveInitialPrompt(admitted, env),
|
|
965
1009
|
});
|
|
966
1010
|
const turnRequest = buildInstructionSeatTurnRequest(admitted, turnProjection);
|
|
967
1011
|
const result = await runPostAdmissionOneShot({
|
|
@@ -1187,6 +1231,10 @@ export async function runPublicInstructionSeat(
|
|
|
1187
1231
|
await persistAdmittedSourceRunPath(admitted, auditorSource, auditorSubject);
|
|
1188
1232
|
admitted = { ...admitted, sourceRunPath: auditorSource } as AdmittedRoleInvocation;
|
|
1189
1233
|
}
|
|
1234
|
+
// #1208 C2: same helper as resume — known board ticket on the audited
|
|
1235
|
+
// source binds and relocates before the missing-ticket soft reask can
|
|
1236
|
+
// fire. True no-ticket sources stay unbound (#1171 asks once).
|
|
1237
|
+
admitted = await inheritKnownTicketFromSource(admitted, env.principalAuthority);
|
|
1190
1238
|
|
|
1191
1239
|
const runAdmitted = async (): Promise<SeatRunResult> => {
|
|
1192
1240
|
await markRunAdmitted(admitted, env.principalAuthority);
|
|
@@ -1235,6 +1283,25 @@ export async function runPublicInstructionSeat(
|
|
|
1235
1283
|
return runAdmitted();
|
|
1236
1284
|
}
|
|
1237
1285
|
|
|
1286
|
+
/**
|
|
1287
|
+
* #1208 C2: same as new source-locator admission — when this leg is still
|
|
1288
|
+
* unbound, inherit the board ticket already on the audited source and relocate.
|
|
1289
|
+
* True no-ticket sources stay unbound so #1171 still asks once.
|
|
1290
|
+
*/
|
|
1291
|
+
async function inheritKnownTicketFromSource(
|
|
1292
|
+
admitted: AdmittedRoleInvocation,
|
|
1293
|
+
authority: DurablePrincipalAuthority,
|
|
1294
|
+
): Promise<AdmittedRoleInvocation> {
|
|
1295
|
+
if (admitted.ticketNumber !== undefined) return admitted;
|
|
1296
|
+
const sourcePath = admittedSourceRunPath(admitted);
|
|
1297
|
+
if (sourcePath === undefined) return admitted;
|
|
1298
|
+
const sourceTicket = await readBoardTicketNumber(sourcePath);
|
|
1299
|
+
if (sourceTicket === undefined) return admitted;
|
|
1300
|
+
await bindAdmittedTicketNumber(admitted, sourceTicket);
|
|
1301
|
+
await relocateAdmittedRunToTicket(admitted, authority);
|
|
1302
|
+
return admitted;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1238
1305
|
export async function runPublicInstructionSeatResume(
|
|
1239
1306
|
request: PublicResumeRequest,
|
|
1240
1307
|
env: InstructionSeatRunEnv,
|
|
@@ -1259,30 +1326,53 @@ export async function runPublicInstructionSeatResume(
|
|
|
1259
1326
|
env.principalAuthority,
|
|
1260
1327
|
effective.runDirectory === undefined ? undefined : { runDirectory: effective.runDirectory },
|
|
1261
1328
|
);
|
|
1329
|
+
let admitted = loaded.admitted;
|
|
1262
1330
|
if (
|
|
1263
|
-
packagedRebindSourceOnResume(
|
|
1331
|
+
packagedRebindSourceOnResume(admitted.role)
|
|
1264
1332
|
&& effective.summons?.sourceRunPath !== undefined
|
|
1265
1333
|
&& effective.summons.sourceRun !== undefined
|
|
1266
1334
|
) {
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
admitted
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
},
|
|
1274
|
-
};
|
|
1335
|
+
// source-locator seats only (notary); sourceRun is on that admission face.
|
|
1336
|
+
admitted = {
|
|
1337
|
+
...admitted,
|
|
1338
|
+
sourceRunPath: effective.summons.sourceRunPath,
|
|
1339
|
+
sourceRun: effective.summons.sourceRun,
|
|
1340
|
+
} as AdmittedRoleInvocation;
|
|
1275
1341
|
}
|
|
1276
|
-
|
|
1342
|
+
admitted = await inheritKnownTicketFromSource(admitted, env.principalAuthority);
|
|
1343
|
+
return { ...loaded, admitted };
|
|
1277
1344
|
},
|
|
1278
1345
|
buildTurnRequest: async (admitted, effective) => {
|
|
1279
1346
|
const summonsPrepared = await prepareSummonsResumeMaterials(admitted.runDirectory, effective.summons);
|
|
1280
|
-
|
|
1281
|
-
admitted,
|
|
1282
|
-
{
|
|
1283
|
-
...resumeTurnRequestProjectionOptions(admitted, effective, env, summonsPrepared),
|
|
1284
|
-
},
|
|
1347
|
+
const projection = resumeTurnRequestProjectionOptions(
|
|
1348
|
+
admitted, effective, env, summonsPrepared,
|
|
1285
1349
|
);
|
|
1350
|
+
// #1208: internal summons only — shared resolver owns review dialogue.
|
|
1351
|
+
// Public bare resume / explicit message keep projection (operational
|
|
1352
|
+
// reasks use the message seam). Do not reselect stale env dialogue over
|
|
1353
|
+
// this-round summons (#1208 R1).
|
|
1354
|
+
if (effective.summons === undefined || effective.message !== undefined) {
|
|
1355
|
+
return buildInstructionSeatTurnRequest(admitted, { ...projection });
|
|
1356
|
+
}
|
|
1357
|
+
const sourceRunPath = admittedSourceRunPath(admitted);
|
|
1358
|
+
const body = await resolveReviewSeatDialogueBody({
|
|
1359
|
+
role: admitted.role,
|
|
1360
|
+
callerInstruction: summonsPrepared?.instruction ?? "",
|
|
1361
|
+
...(sourceRunPath === undefined ? {} : { sourceRunPath }),
|
|
1362
|
+
projectRoot: admitted.projectRoot,
|
|
1363
|
+
home: env.home,
|
|
1364
|
+
});
|
|
1365
|
+
if (body === undefined) {
|
|
1366
|
+
return buildInstructionSeatTurnRequest(admitted, { ...projection });
|
|
1367
|
+
}
|
|
1368
|
+
const fileFlags = summonsPrepared?.attachments ?? [];
|
|
1369
|
+
const prompt = fileFlags.length === 0
|
|
1370
|
+
? body
|
|
1371
|
+
: appendCallerFileFlagPaths(body, fileFlags);
|
|
1372
|
+
return buildInstructionSeatTurnRequest(admitted, {
|
|
1373
|
+
...projection,
|
|
1374
|
+
continuation: { kind: "resume", prompt },
|
|
1375
|
+
});
|
|
1286
1376
|
},
|
|
1287
1377
|
adapters: {
|
|
1288
1378
|
trySettle: async () => undefined,
|
|
@@ -1361,9 +1451,12 @@ async function queueConclusionFromChild(
|
|
|
1361
1451
|
return { stop: { exitCode: 0, ...(terminal === undefined ? {} : { terminal }) } };
|
|
1362
1452
|
}
|
|
1363
1453
|
let current: AdmittedRoleInvocation = child;
|
|
1364
|
-
//
|
|
1365
|
-
//
|
|
1366
|
-
//
|
|
1454
|
+
// #1208: review-queue gate children also have seat-local
|
|
1455
|
+
// POST_SUBMISSION_ROUTING. This parent-side loop remains the
|
|
1456
|
+
// gate/correlation budget; nestedIncomplete from an exhausted seat-local
|
|
1457
|
+
// chain surfaces without a second spend. The child resume keeps the
|
|
1458
|
+
// caller's env so delivery, failure recovery, and other reask loops
|
|
1459
|
+
// stay separate.
|
|
1367
1460
|
let budgetEnv: InstructionSeatRunEnv = envForCourtContinue(env);
|
|
1368
1461
|
for (;;) {
|
|
1369
1462
|
const terminal = await trySettlePublicSeat(
|
|
@@ -1548,7 +1641,8 @@ async function auditSubmittedRole(
|
|
|
1548
1641
|
return runPublicInstructionSeatResume({
|
|
1549
1642
|
runId: admitted.runId,
|
|
1550
1643
|
runDirectory: admitted.runDirectory,
|
|
1551
|
-
|
|
1644
|
+
message: SECRETARIAT_STATUS_REASK,
|
|
1645
|
+
summons: { instructionEmpty: true },
|
|
1552
1646
|
}, next, io);
|
|
1553
1647
|
}
|
|
1554
1648
|
if (admitted.role === "countersign" && (typeof status !== "string" || !REVIEW_QUEUE_STATUSES.has(status))) {
|
|
@@ -1562,7 +1656,8 @@ async function auditSubmittedRole(
|
|
|
1562
1656
|
return runPublicInstructionSeatResume({
|
|
1563
1657
|
runId: admitted.runId,
|
|
1564
1658
|
runDirectory: admitted.runDirectory,
|
|
1565
|
-
|
|
1659
|
+
message: officerConclusionReask(status),
|
|
1660
|
+
summons: { instructionEmpty: true },
|
|
1566
1661
|
}, next, io);
|
|
1567
1662
|
}
|
|
1568
1663
|
// Self-escalation is a valid open routing state for these seats only.
|
|
@@ -1839,7 +1839,7 @@ type InstructionTransportSource = {
|
|
|
1839
1839
|
|
|
1840
1840
|
function admittedTransportPromptKind(
|
|
1841
1841
|
admitted: InstructionTransportSource,
|
|
1842
|
-
): "instruction" | "
|
|
1842
|
+
): "instruction" | "baseline" | "skill-args" {
|
|
1843
1843
|
if (admitted.role === undefined) return "instruction";
|
|
1844
1844
|
const record = packagedRoleMetadata(admitted.role);
|
|
1845
1845
|
if (record !== undefined && "transportPrompt" in record) return record.transportPrompt;
|
|
@@ -1870,19 +1870,15 @@ export function appendCallerFileFlagPaths(
|
|
|
1870
1870
|
|
|
1871
1871
|
/**
|
|
1872
1872
|
* One initial prompt transport. The registry `transportPrompt` leaf selects
|
|
1873
|
-
* a
|
|
1874
|
-
*
|
|
1873
|
+
* a bound baseline or skill args. Absent means the caller instruction plus
|
|
1874
|
+
* caller file-flag paths (ADR 0087). Notary dialogue is owned by
|
|
1875
|
+
* resolveReviewSeatDialogueBody (review fixed dispatch), not an empty transport leaf.
|
|
1875
1876
|
* Engine / outsourcing material rides startup readingMaterial (#1167), not here.
|
|
1876
1877
|
*/
|
|
1877
1878
|
export function buildInstructionTransportPrompt(
|
|
1878
1879
|
admitted: InstructionTransportSource,
|
|
1879
1880
|
): string {
|
|
1880
1881
|
const kind = admittedTransportPromptKind(admitted);
|
|
1881
|
-
if (kind === "fixed-kickoff") {
|
|
1882
|
-
// #1166 / ADR 0087: audited-run identity rides startup materials; dialogue
|
|
1883
|
-
// prompt is peer body / reask only — never a package-computed directory path.
|
|
1884
|
-
return "";
|
|
1885
|
-
}
|
|
1886
1882
|
if (kind === "baseline") {
|
|
1887
1883
|
if (admitted.baseRevision === undefined) {
|
|
1888
1884
|
throw new Error("baseline transport prompt is missing the bound revision");
|
|
@@ -606,8 +606,8 @@ const NOTARY_OPTIONS = [
|
|
|
606
606
|
repeatable: false,
|
|
607
607
|
form: "option",
|
|
608
608
|
description: {
|
|
609
|
-
en: "Required source run locator (runId@role under the book home, or path to that run directory).
|
|
610
|
-
zh: "必填源 run 定位符(簿内 runId@role,或该 run
|
|
609
|
+
en: "Required source run locator (runId@role under the book home, or path to that run directory). Binds the audited identity; review dialogue is the fixed Notary dispatch (verdict stays in the ledger). Missing-ticket and unreadable-status reasks keep their existing diagnostic text.",
|
|
610
|
+
zh: "必填源 run 定位符(簿内 runId@role,或该 run 目录路径)。绑定受审身份;审稿对话为符宝郎固定派单文(判词留在卷宗自行读取)。缺票号与读不出 status 的重交仍递既有诊断原文。",
|
|
611
611
|
},
|
|
612
612
|
},
|
|
613
613
|
] as const satisfies readonly PublicOptionDefinition[];
|
|
@@ -1256,7 +1256,8 @@ const ROLE_COMMAND_HELP = {
|
|
|
1256
1256
|
},
|
|
1257
1257
|
notary: {
|
|
1258
1258
|
command: "notary",
|
|
1259
|
-
summary:
|
|
1259
|
+
summary:
|
|
1260
|
+
"Direct Notary summons; duties: souls/notary.md. Review uses fixed dispatch — binds --source-run identity; does not accept caller review prompt; verdict stays in the ledger. Missing-ticket (#1171) and unreadable-status reasks deliver their existing diagnostic text on the same seat, not the review sentence.",
|
|
1260
1261
|
usage: ["ak-role notary --source-run <runId@role|path> [options]"],
|
|
1261
1262
|
examples: [
|
|
1262
1263
|
"ak-role notary --source-run 01a034f1-75bf-71a6-bcf5-d1299145b1a5@judge",
|
|
@@ -1319,7 +1320,7 @@ const SUPPORT_COMMAND_HELP = {
|
|
|
1319
1320
|
resume: {
|
|
1320
1321
|
command: "resume",
|
|
1321
1322
|
summary:
|
|
1322
|
-
"Resume a role run under the live seat table (model/host/engine); session principal must still exist. Every callable seat
|
|
1323
|
+
"Resume a role run under the live seat table (model/host/engine); session principal must still exist. Every callable seat accepts the optional caller message and passes it through unchanged as the continuation prompt. Notary new/direct review calls use only --source-run plus the fixed review dispatch (no caller review prompt); public manual resume still passes the caller message. Global --model/--thinking/--host/--engine must be placed before <runId> (either before `resume` or between `resume` and <runId>); the one argv after <runId> is the opaque message, not a flag position (#471).",
|
|
1323
1324
|
usage: ["ak-role resume <runId> [message]"],
|
|
1324
1325
|
examples: [
|
|
1325
1326
|
"ak-role resume 01abc…",
|
|
@@ -9,11 +9,9 @@ import { randomUUID } from "node:crypto";
|
|
|
9
9
|
import { writeFile } from "node:fs/promises";
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
|
-
buildAutoResumeContinuationPrompt,
|
|
13
12
|
findRunDirectoryById,
|
|
14
13
|
resolveLiveRunDirectoryPath,
|
|
15
14
|
readRoleRunState,
|
|
16
|
-
RESUME_TRANSPORT_ENVELOPE,
|
|
17
15
|
type PublicResumeRequest,
|
|
18
16
|
type SameTicketSummonsMaterials,
|
|
19
17
|
} from "./run-lifecycle.ts";
|
|
@@ -820,17 +818,14 @@ function settlementScopeForTurn(request: RoleTurnRequest): SettlementCourtScope
|
|
|
820
818
|
}
|
|
821
819
|
|
|
822
820
|
/**
|
|
823
|
-
* #1132: one 没交卷催交 turn, dispatched through the SAME settlement path
|
|
824
|
-
* first turn.
|
|
821
|
+
* #1132 / #1208: one 没交卷催交 turn, dispatched through the SAME settlement path
|
|
822
|
+
* as the first turn.
|
|
825
823
|
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
831
|
-
* `settleCompletedHostTurn` the first turn uses (ADR 0080): the runner/host
|
|
832
|
-
* failure re-read, the shouldPresent gate, the fresh-seal check, open-court
|
|
833
|
-
* cleanup, cancel re-reads, run-state persist, and the stderr mirror.
|
|
824
|
+
* A delivery turn is an ordinary host turn on this same run/session: this builds
|
|
825
|
+
* the host-neutral resume request (the adapter's stored native session id,
|
|
826
|
+
* ADR 0082). #1208: resend this turn's actual prompt — never substitute
|
|
827
|
+
* delivery-state / run-stats JSON for dialogue. Counts, rejections, and
|
|
828
|
+
* no_receipt facts stay on receiptDelivery / lifecycle settlement.
|
|
834
829
|
*
|
|
835
830
|
* The caller owns the live delivery policy; the request projects its next send.
|
|
836
831
|
*/
|
|
@@ -844,10 +839,7 @@ function buildReceiptDeliveryRequest(input: {
|
|
|
844
839
|
...request,
|
|
845
840
|
continuation: {
|
|
846
841
|
kind: "resume",
|
|
847
|
-
prompt:
|
|
848
|
-
...receiptDelivery.deliveryState(),
|
|
849
|
-
deliveryTurns: receiptDelivery.issuedDeliveryRequests() + 1,
|
|
850
|
-
}),
|
|
842
|
+
prompt: request.continuation.prompt,
|
|
851
843
|
},
|
|
852
844
|
deliveryRequestLimit: receiptDelivery.limit,
|
|
853
845
|
};
|
|
@@ -1723,8 +1715,8 @@ export function resumeTurnRequestProjectionOptions(
|
|
|
1723
1715
|
? body
|
|
1724
1716
|
: appendCallerFileFlagPaths(body, fileFlags);
|
|
1725
1717
|
} else if (request.summons !== undefined) {
|
|
1726
|
-
//
|
|
1727
|
-
//
|
|
1718
|
+
// Same-ticket summons without prepared dialogue. Notary review fills via
|
|
1719
|
+
// resolveReviewSeatDialogueBody (#1208); other seats carry parent/caller words.
|
|
1728
1720
|
prompt = "";
|
|
1729
1721
|
} else {
|
|
1730
1722
|
prompt = "";
|
|
@@ -2050,21 +2042,15 @@ export async function runPostAdmissionSeatResume<
|
|
|
2050
2042
|
buildInitialPayload: (): StationChildAttempt => ({ resumeTurn: false }),
|
|
2051
2043
|
buildResumePayload: (): StationChildAttempt => ({ resumeTurn: true }),
|
|
2052
2044
|
toRequest: async (payload) => {
|
|
2053
|
-
// #840 r8
|
|
2054
|
-
//
|
|
2055
|
-
//
|
|
2056
|
-
// host-needed resume trigger — never engine handbook material,
|
|
2057
|
-
// which would replace a 审核循环 same-ticket continuation with a bare outsourcing
|
|
2058
|
-
// 「重新读」 envelope (#755 contract, resumeTurnRequestProjectionOptions
|
|
2059
|
-
// above). RESUME_TRANSPORT_ENVELOPE is the same package-owned,
|
|
2060
|
-
// non-semantic trigger that projection already uses for a
|
|
2061
|
-
// same-ticket summons carrying no instruction/attachments.
|
|
2045
|
+
// #840 r8 / #1208: call-local retry keeps this court's frozen summons /
|
|
2046
|
+
// 交卷 body / attachments and resends the same turn prompt — never a
|
|
2047
|
+
// filler phrase or engine handbook substitute.
|
|
2062
2048
|
if (payload.resumeTurn && firstTurn !== undefined) {
|
|
2063
2049
|
return {
|
|
2064
2050
|
...firstTurn,
|
|
2065
2051
|
continuation: {
|
|
2066
2052
|
kind: "resume",
|
|
2067
|
-
prompt:
|
|
2053
|
+
prompt: firstTurn.continuation.prompt,
|
|
2068
2054
|
},
|
|
2069
2055
|
};
|
|
2070
2056
|
}
|
|
@@ -2227,6 +2213,7 @@ export async function runPostAdmissionOneShot<
|
|
|
2227
2213
|
admitted?: A;
|
|
2228
2214
|
terminal?: T;
|
|
2229
2215
|
}> {
|
|
2216
|
+
// #1208: auto-resume resends this turn's actual prompt, never a filler phrase.
|
|
2230
2217
|
return await runPostAdmissionResumable({
|
|
2231
2218
|
admitted: input.admitted,
|
|
2232
2219
|
env: input.env,
|
|
@@ -2236,7 +2223,7 @@ export async function runPostAdmissionOneShot<
|
|
|
2236
2223
|
...input.request,
|
|
2237
2224
|
continuation: {
|
|
2238
2225
|
kind: "resume",
|
|
2239
|
-
prompt:
|
|
2226
|
+
prompt: input.request.continuation.prompt,
|
|
2240
2227
|
},
|
|
2241
2228
|
}),
|
|
2242
2229
|
adapters: input.adapters,
|