@akagilnc/pi-workflow-roles 0.1.5013 → 0.1.5037
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 +116 -101
- package/dist/headless-host/production-host.js +116 -101
- package/dist/migrate-book-topology.js +3 -2
- package/dist/packaged-role-registry.js +3 -2
- package/dist/public-cli/cli.js +17 -2
- package/dist/public-cli/countersign-run.js +13 -8
- package/dist/public-cli/instruction-seat-run.js +63 -67
- package/dist/public-cli/invocation.js +13 -32
- package/dist/public-cli/main.js +172 -101
- package/dist/public-cli/post-admission.js +8 -5
- package/dist/public-cli/run-lifecycle.js +12 -15
- package/dist/public-cli/seat-ticket-binding.js +5 -8
- package/dist/public-role-summons.js +8 -3
- package/package.json +1 -1
- package/resources/countersign-ticket-issue.md +5 -0
- package/resources/diarist-collect.md +1 -0
- package/resources/secretariat-ticket-identity.md +15 -0
- package/souls/audit-law.md +1 -1
- package/src/packaged-role-registry.ts +3 -2
- package/src/public-cli/cli.ts +18 -2
- package/src/public-cli/countersign-run.ts +17 -10
- package/src/public-cli/instruction-seat-run.ts +66 -63
- package/src/public-cli/invocation.ts +10 -38
- package/src/public-cli/post-admission.ts +10 -6
- package/src/public-cli/run-lifecycle.ts +12 -13
- package/src/public-cli/seat-ticket-binding.ts +5 -8
- package/src/public-role-summons.ts +16 -3
|
@@ -258,9 +258,7 @@ async function runCountersignBody(parsed, env, io) {
|
|
|
258
258
|
projectRoot: resolve(parsed.project ?? env.cwd),
|
|
259
259
|
role: "countersign",
|
|
260
260
|
parentRunPath: gateParentRunPath,
|
|
261
|
-
...(env.boundTicketNumber
|
|
262
|
-
? {}
|
|
263
|
-
: { ticketNumber: env.boundTicketNumber }),
|
|
261
|
+
...(isSafePositiveTicketNumber(env.boundTicketNumber) ? { ticketNumber: env.boundTicketNumber } : {}),
|
|
264
262
|
freshSummons: env.freshSummons,
|
|
265
263
|
summons: {
|
|
266
264
|
sourceRunPath: gateParentRunPath,
|
|
@@ -296,14 +294,16 @@ async function runCountersignBody(parsed, env, io) {
|
|
|
296
294
|
let typedTicket;
|
|
297
295
|
let typedCourtTicketNumbers;
|
|
298
296
|
let identityDiaristRan = false;
|
|
299
|
-
let
|
|
300
|
-
|
|
297
|
+
let childDiaristRunId;
|
|
298
|
+
let pausedDiaristTerminal;
|
|
299
|
+
if (gateParentRunPath === undefined && env.runCourtDiaristStation === undefined) {
|
|
301
300
|
let outcome;
|
|
302
301
|
try {
|
|
303
302
|
outcome = await invokeCourtDiarist({
|
|
304
303
|
instruction: parsed.instruction ?? "",
|
|
305
304
|
projectRoot: admitted.projectRoot,
|
|
306
305
|
failureLabel: "unbound summons",
|
|
306
|
+
correlationId: admitted.runId,
|
|
307
307
|
...(env.boundTicketNumber === undefined ? {} : { boundTicketNumber: env.boundTicketNumber }),
|
|
308
308
|
}, env, io);
|
|
309
309
|
}
|
|
@@ -318,16 +318,16 @@ async function runCountersignBody(parsed, env, io) {
|
|
|
318
318
|
}, seatAdapters(admitted, env), env.principalAuthority, io);
|
|
319
319
|
}
|
|
320
320
|
identityDiaristRan = true;
|
|
321
|
+
if (outcome.identity.kind === "escalate")
|
|
322
|
+
pausedDiaristTerminal = outcome.terminal;
|
|
321
323
|
if (outcome.admitted?.runDirectory.includes(`${sep}unbound${sep}runs${sep}`)) {
|
|
322
|
-
|
|
324
|
+
childDiaristRunId = outcome.admitted.runId;
|
|
323
325
|
}
|
|
324
|
-
if (outcome.
|
|
325
|
-
const diagnostic = outcome.
|
|
326
|
-
? outcome.identity.diagnostic
|
|
327
|
-
: outcome.failedWithoutEscalate?.diagnostic ?? "";
|
|
326
|
+
if (outcome.failedWithoutEscalate !== undefined) {
|
|
327
|
+
const diagnostic = outcome.failedWithoutEscalate.diagnostic;
|
|
328
328
|
await materializeAdmission(isSafePositiveTicketNumber(env.boundTicketNumber) ? env.boundTicketNumber : undefined);
|
|
329
|
-
if (
|
|
330
|
-
await recordChildDiaristRun(admitted,
|
|
329
|
+
if (childDiaristRunId !== undefined)
|
|
330
|
+
await recordChildDiaristRun(admitted, childDiaristRunId);
|
|
331
331
|
await markRunAdmitted(admitted, env.principalAuthority);
|
|
332
332
|
return await presentControlledFailure(admitted, {
|
|
333
333
|
timedOut: false,
|
|
@@ -337,19 +337,19 @@ async function runCountersignBody(parsed, env, io) {
|
|
|
337
337
|
}, seatAdapters(admitted, env), env.principalAuthority, io);
|
|
338
338
|
}
|
|
339
339
|
if (outcome.identity.kind === "ticket") {
|
|
340
|
-
typedTicket =
|
|
341
|
-
? env.boundTicketNumber
|
|
342
|
-
: outcome.identity.ticketNumber;
|
|
340
|
+
typedTicket = outcome.identity.ticketNumber;
|
|
343
341
|
typedCourtTicketNumbers = outcome.identity.courtTicketNumbers;
|
|
344
342
|
}
|
|
345
|
-
else if (isSafePositiveTicketNumber(env.boundTicketNumber)) {
|
|
346
|
-
typedTicket = env.boundTicketNumber;
|
|
347
|
-
}
|
|
348
343
|
}
|
|
349
344
|
await materializeAdmission(typedTicket);
|
|
350
345
|
await markRunAdmitted(admitted, env.principalAuthority);
|
|
351
|
-
if (
|
|
352
|
-
await recordChildDiaristRun(admitted,
|
|
346
|
+
if (childDiaristRunId !== undefined) {
|
|
347
|
+
await recordChildDiaristRun(admitted, childDiaristRunId);
|
|
348
|
+
}
|
|
349
|
+
if (pausedDiaristTerminal !== undefined) {
|
|
350
|
+
// The child already submitted its own pause; no parent turn exists yet.
|
|
351
|
+
io.stdout(formatTerminalResult(pausedDiaristTerminal));
|
|
352
|
+
return { exitCode: 0, admitted, terminal: pausedDiaristTerminal };
|
|
353
353
|
}
|
|
354
354
|
if (gateParentRunPath !== undefined) {
|
|
355
355
|
await persistAdmittedSourceRunPath(admitted, gateParentRunPath);
|
|
@@ -401,7 +401,11 @@ async function runCountersignBody(parsed, env, io) {
|
|
|
401
401
|
if (!packagedAdmitsCountersign(admittedSeat.role))
|
|
402
402
|
return;
|
|
403
403
|
if (!identityDiaristRan || typedTicket !== undefined) {
|
|
404
|
-
await runCountersignCourtDiaristStation(admittedSeat, env, io);
|
|
404
|
+
const refresh = await runCountersignCourtDiaristStation(admittedSeat, env, io);
|
|
405
|
+
if (refresh !== undefined) {
|
|
406
|
+
io.stdout(formatTerminalResult(refresh.terminal));
|
|
407
|
+
return refresh.terminal;
|
|
408
|
+
}
|
|
405
409
|
}
|
|
406
410
|
await relocateAdmittedRunToTicket(admittedSeat, env.principalAuthority, lease);
|
|
407
411
|
Object.assign(turnRequest, buildInstructionSeatTurnRequest(admittedSeat, turnProjection));
|
|
@@ -486,7 +490,6 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
486
490
|
projectRoot,
|
|
487
491
|
role,
|
|
488
492
|
parentRunPath: sourceDirectory,
|
|
489
|
-
...(auditorTicket === undefined ? {} : { ticketNumber: auditorTicket }),
|
|
490
493
|
freshSummons: env.freshSummons,
|
|
491
494
|
summons,
|
|
492
495
|
resume: (runId, materials) => runPublicInstructionSeatResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, env, io),
|
|
@@ -511,7 +514,6 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
511
514
|
projectRoot,
|
|
512
515
|
role,
|
|
513
516
|
parentRunPath,
|
|
514
|
-
...(env.boundTicketNumber === undefined ? {} : { ticketNumber: env.boundTicketNumber }),
|
|
515
517
|
freshSummons: env.freshSummons,
|
|
516
518
|
summons,
|
|
517
519
|
resume: (runId, materials) => runPublicInstructionSeatResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, env, io),
|
|
@@ -537,7 +539,6 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
537
539
|
throw error;
|
|
538
540
|
}
|
|
539
541
|
const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
|
|
540
|
-
const knownTicket = (await readBoardTicketNumber(source.runDirectory)) ?? env.boundTicketNumber;
|
|
541
542
|
const summons = {
|
|
542
543
|
sourceRunPath: source.runDirectory,
|
|
543
544
|
sourceRun: source,
|
|
@@ -548,7 +549,6 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
548
549
|
projectRoot,
|
|
549
550
|
role,
|
|
550
551
|
parentRunPath: source.runDirectory,
|
|
551
|
-
...(knownTicket === undefined ? {} : { ticketNumber: knownTicket }),
|
|
552
552
|
freshSummons: env.freshSummons,
|
|
553
553
|
summons,
|
|
554
554
|
resume: (runId, materials) => runPublicInstructionSeatResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, env, io),
|
|
@@ -579,54 +579,34 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
579
579
|
}
|
|
580
580
|
const runAdmitted = async () => {
|
|
581
581
|
await markRunAdmitted(admitted, env.principalAuthority);
|
|
582
|
-
if (
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}, io);
|
|
582
|
+
if (role === "secretariat") {
|
|
583
|
+
let outcome;
|
|
584
|
+
try {
|
|
585
|
+
outcome = await invokeCourtDiarist({
|
|
586
|
+
instruction: parsed.instruction ?? "",
|
|
587
|
+
projectRoot: admitted.projectRoot,
|
|
588
|
+
failureLabel: "secretariat unbound summons",
|
|
589
|
+
attachmentPaths: admitted.attachments.map((attachment) => attachment.frozenPath),
|
|
590
|
+
correlationId: admitted.runId,
|
|
591
|
+
}, env, io);
|
|
592
|
+
}
|
|
593
|
+
catch (error) {
|
|
594
|
+
return await presentControlledFailure(admitted, {
|
|
595
|
+
timedOut: false, code: null, stderr: "", thrown: error,
|
|
596
|
+
}, seatAdapters(admitted, env), env.principalAuthority, io);
|
|
597
|
+
}
|
|
599
598
|
if (outcome.admitted?.runDirectory.includes(`${sep}unbound${sep}runs${sep}`)) {
|
|
600
599
|
await recordChildDiaristRun(admitted, outcome.admitted.runId);
|
|
601
600
|
}
|
|
602
|
-
if (outcome.
|
|
603
|
-
const diagnostic = outcome.identity.kind === "escalate"
|
|
604
|
-
? outcome.identity.diagnostic
|
|
605
|
-
: outcome.failedWithoutEscalate?.diagnostic ?? "";
|
|
601
|
+
if (outcome.failedWithoutEscalate !== undefined) {
|
|
606
602
|
return await presentControlledFailure(admitted, {
|
|
607
|
-
timedOut: false,
|
|
608
|
-
|
|
609
|
-
stderr: "",
|
|
610
|
-
thrown: new Error(diagnostic),
|
|
603
|
+
timedOut: false, code: null, stderr: "",
|
|
604
|
+
thrown: new Error(outcome.failedWithoutEscalate.diagnostic),
|
|
611
605
|
}, seatAdapters(admitted, env), env.principalAuthority, io);
|
|
612
606
|
}
|
|
613
|
-
if (outcome.identity.kind === "
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
throw new Error("court diarist ticket has no admitted run");
|
|
617
|
-
await bindAdmittedTicketNumber(outcome.admitted, outcome.identity.ticketNumber);
|
|
618
|
-
await relocateAdmittedRunToTicket(outcome.admitted, env.principalAuthority);
|
|
619
|
-
await bindAdmittedTicketNumber(admitted, outcome.identity.ticketNumber);
|
|
620
|
-
await relocateAdmittedRunToTicket(admitted, env.principalAuthority);
|
|
621
|
-
}
|
|
622
|
-
catch (error) {
|
|
623
|
-
return await presentControlledFailure(admitted, {
|
|
624
|
-
timedOut: false,
|
|
625
|
-
code: null,
|
|
626
|
-
stderr: "",
|
|
627
|
-
thrown: error,
|
|
628
|
-
}, seatAdapters(admitted, env), env.principalAuthority, io);
|
|
629
|
-
}
|
|
607
|
+
if (outcome.identity.kind === "escalate" && outcome.terminal !== undefined) {
|
|
608
|
+
io.stdout(formatTerminalResult(outcome.terminal));
|
|
609
|
+
return { exitCode: 0, admitted, terminal: outcome.terminal };
|
|
630
610
|
}
|
|
631
611
|
}
|
|
632
612
|
return dispatchAdmitted(admitted, env, io);
|
|
@@ -689,3 +669,19 @@ export async function runPublicInstructionSeatResume(request, env, io) {
|
|
|
689
669
|
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
690
670
|
});
|
|
691
671
|
}
|
|
672
|
+
/** Continue the parent once any escalated child has submitted. */
|
|
673
|
+
export async function continueParentAfterChild(parentRunId, child, env, io) {
|
|
674
|
+
const loaded = await loadResumablePublicRole(env.home, parentRunId, env.principalAuthority, true);
|
|
675
|
+
if (loaded.run.state !== "admitted") {
|
|
676
|
+
return runPublicInstructionSeatResume({ runId: parentRunId }, env, io);
|
|
677
|
+
}
|
|
678
|
+
const admitted = loaded.admitted;
|
|
679
|
+
if (admitted.role === "countersign" && admitted.ticketNumber !== undefined) {
|
|
680
|
+
const refresh = await runCountersignCourtDiaristStation(admitted, env, io, child.ticketNumber);
|
|
681
|
+
if (refresh !== undefined) {
|
|
682
|
+
io.stdout(formatTerminalResult(refresh.terminal));
|
|
683
|
+
return { exitCode: 0, admitted, terminal: refresh.terminal };
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return dispatchAdmitted(admitted, env, io);
|
|
687
|
+
}
|
|
@@ -9,7 +9,7 @@ import { tmpdir } from "node:os";
|
|
|
9
9
|
import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
10
10
|
import { activationBookDirectory, ensureRealDirectoryTree, homeFromRunDirectory, pathContainedIn, resolveActivationLedgerHome, } from "../activation-ledger-topology.js";
|
|
11
11
|
import { resolveBookKeyFromGit } from "../activation-ledger-git.js";
|
|
12
|
-
import { ensureRoleRunDirectory, ensureRoleRunPlacement, roleRunArtifactsDirectory, roleRunPlacement, } from "../role-run-placement.js";
|
|
12
|
+
import { ensureRoleRunDirectory, ensureRoleRunPlacement, listBookRunDirectories, roleRunArtifactsDirectory, roleRunPlacement, } from "../role-run-placement.js";
|
|
13
13
|
import { packagedAdmittedSubject, packagedArgvResult, packagedBareToken, packagedBaseIsRevision, packagedEmitsAuthorityRefs, packagedPublicInstructionSubject, packagedRoleMetadata, packagedStoresSourceRunRaw, packagedSubjectChoices, } from "../packaged-role-registry.js";
|
|
14
14
|
import { isSafePositiveTicketNumber, readBoardTicketNumber, requireSafePositiveTicketNumber, } from "../run-ticket-number.js";
|
|
15
15
|
import { rewriteRunDirectoryPathFields, rewriteRunDirectoryPathValue, } from "../role-run-relocation.js";
|
|
@@ -164,15 +164,11 @@ export async function persistAdmittedSourceRunPath(admitted, sourceRunPath) {
|
|
|
164
164
|
/**
|
|
165
165
|
* Bind a post-admission resolved ticketNumber onto the in-memory admitted
|
|
166
166
|
* object and both durable pages (invocation.json + admitted-request.json).
|
|
167
|
-
*
|
|
168
|
-
* Never clears an existing binding.
|
|
167
|
+
* A later typed role receipt replaces an earlier assertion (#1025).
|
|
169
168
|
*/
|
|
170
169
|
export async function bindAdmittedTicketNumber(admitted, ticketNumber) {
|
|
171
|
-
if (admitted.ticketNumber
|
|
172
|
-
|
|
173
|
-
return;
|
|
174
|
-
throw new Error(`bindAdmittedTicketNumber refuses to replace existing ticket #${admitted.ticketNumber} with #${ticketNumber}`);
|
|
175
|
-
}
|
|
170
|
+
if (admitted.ticketNumber === ticketNumber)
|
|
171
|
+
return;
|
|
176
172
|
await bindTicketNumberOnRunDirectory(admitted.runDirectory, ticketNumber);
|
|
177
173
|
admitted.ticketNumber = ticketNumber;
|
|
178
174
|
}
|
|
@@ -229,7 +225,7 @@ export async function bindCourtTicketNumbersOnAdmitted(admitted, courtTicketNumb
|
|
|
229
225
|
});
|
|
230
226
|
admitted.courtTicketNumbers = frozen;
|
|
231
227
|
}
|
|
232
|
-
/**
|
|
228
|
+
/** File a role run under its latest typed ticket identity. */
|
|
233
229
|
export async function relocateAdmittedRunToTicket(admitted, authority, heldLease) {
|
|
234
230
|
if (admitted.ticketNumber === undefined || !admitted.runDirectory.includes(`${sep}unbound${sep}runs${sep}`))
|
|
235
231
|
return undefined;
|
|
@@ -252,6 +248,9 @@ export async function relocateAdmittedRunToTicket(admitted, authority, heldLease
|
|
|
252
248
|
if (typeof childRunId !== "string")
|
|
253
249
|
continue;
|
|
254
250
|
const childDirectory = join(dirname(oldRunDirectory), `${childRunId}@diarist`);
|
|
251
|
+
// A child that already filed under a ticket keeps its own assertion.
|
|
252
|
+
if (!existsSync(childDirectory))
|
|
253
|
+
continue;
|
|
255
254
|
const childTarget = roleRunPlacement(ledgerHome, {
|
|
256
255
|
bookKey: admitted.bookKey,
|
|
257
256
|
subject: { ticketNumber: admitted.ticketNumber },
|
|
@@ -259,21 +258,18 @@ export async function relocateAdmittedRunToTicket(admitted, authority, heldLease
|
|
|
259
258
|
role: "diarist",
|
|
260
259
|
});
|
|
261
260
|
authority.seal(childTarget);
|
|
262
|
-
if (!existsSync(childDirectory) && existsSync(childTarget.runDirectory))
|
|
263
|
-
continue;
|
|
264
261
|
await bindTicketNumberOnRunDirectory(childDirectory, admitted.ticketNumber);
|
|
265
262
|
await rehomeUnboundTicketProvenance(childDirectory, admitted.ticketNumber, admitted.projectRoot, homeFromRunDirectory(oldRunDirectory));
|
|
266
263
|
ensureRoleRunDirectory(ledgerHome, dirname(childTarget.runDirectory));
|
|
267
264
|
await rename(childDirectory, childTarget.runDirectory);
|
|
268
265
|
}
|
|
269
266
|
}
|
|
270
|
-
//
|
|
271
|
-
// own bind and the caller's later bind use this existing relocation seam.
|
|
267
|
+
// The first ticket assignment moves only an unbound diarist's own diary.
|
|
272
268
|
if (admitted.role === "diarist") {
|
|
273
269
|
await rehomeUnboundTicketProvenance(oldRunDirectory, admitted.ticketNumber, admitted.projectRoot, homeFromRunDirectory(oldRunDirectory));
|
|
274
270
|
}
|
|
275
|
-
// Rename commits the run placement. Diary assignment above is
|
|
276
|
-
//
|
|
271
|
+
// Rename commits the run placement. Diary assignment above is a Sitian append
|
|
272
|
+
// outside the atomic directory move.
|
|
277
273
|
// Persisted paths are resolved from typed run identity on read.
|
|
278
274
|
await rename(oldRunDirectory, target.runDirectory);
|
|
279
275
|
// rename moved the open lock inode with the directory. Transfer cleanup
|
|
@@ -312,24 +308,9 @@ export async function recordChildDiaristRun(parent, childRunId) {
|
|
|
312
308
|
export async function bindTicketNumberOnRunDirectory(runDirectory, ticketNumber) {
|
|
313
309
|
requireSafePositiveTicketNumber(ticketNumber, "bindTicketNumberOnRunDirectory");
|
|
314
310
|
const admittedPath = join(runDirectory, "admitted-request.json");
|
|
315
|
-
const invocationPath = join(runDirectory, "invocation.json");
|
|
316
311
|
const admitted = JSON.parse(await readFile(admittedPath, "utf8"));
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if (existing === ticketNumber)
|
|
320
|
-
return;
|
|
321
|
-
throw new Error(`bindTicketNumberOnRunDirectory refuses to replace existing ticket #${existing} with #${ticketNumber}`);
|
|
322
|
-
}
|
|
323
|
-
// Conflict guard before any write: crash window of bindAdmittedTicketNumber
|
|
324
|
-
// can leave invocation bound while admitted-request is still unbound — refuse
|
|
325
|
-
// silent rebind. Read-before-merge; never check the page just overwritten.
|
|
326
|
-
if (existsSync(invocationPath)) {
|
|
327
|
-
const invocation = JSON.parse(await readFile(invocationPath, "utf8"));
|
|
328
|
-
if (typeof invocation.ticketNumber === "number" &&
|
|
329
|
-
invocation.ticketNumber !== ticketNumber) {
|
|
330
|
-
throw new Error(`bindTicketNumberOnRunDirectory refuses to replace invocation ticket #${invocation.ticketNumber} with #${ticketNumber}`);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
312
|
+
if (admitted.ticketNumber === ticketNumber)
|
|
313
|
+
return;
|
|
333
314
|
await writeFile(admittedPath, `${JSON.stringify({ ...admitted, ticketNumber }, null, 2)}\n`, "utf8");
|
|
334
315
|
await mergeInvocationIdentityPage(runDirectory, { ticketNumber });
|
|
335
316
|
}
|