@akagilnc/pi-workflow-roles 0.1.5357 → 0.1.5368
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 +5353 -5480
- package/dist/doctor-contracts.js +22 -34
- package/dist/headless-host/production-host.js +1215 -1342
- package/dist/merger-contracts.js +11 -6
- package/dist/migrate-book-topology.js +57 -101
- package/dist/open-tool-schema.js +1 -25
- package/dist/package-contracts/fixer-output.js +21 -35
- package/dist/public-cli/auto-resume.js +1 -1
- package/dist/public-cli/cli.js +57 -56
- package/dist/public-cli/config.js +4 -0
- package/dist/public-cli/instruction-seat-run.js +65 -40
- package/dist/public-cli/invocation.js +132 -128
- package/dist/public-cli/main.js +449 -506
- package/dist/public-cli/post-admission.js +85 -86
- package/dist/public-cli/role-seat-runtime.js +29 -0
- package/dist/public-cli/run-lifecycle.js +6 -6
- package/dist/public-cli/turn-request.js +14 -0
- package/dist/public-role-summons.js +20 -33
- package/dist/role-run-placement.js +4 -0
- package/package.json +1 -1
- package/src/book-topology-migration-placement.ts +3 -5
- package/src/diarist-role.ts +12 -49
- package/src/doctor-contracts.ts +38 -49
- package/src/gleaner-left-role.ts +10 -11
- package/src/merger-contracts.ts +19 -12
- package/src/open-tool-schema.ts +8 -32
- package/src/package-contracts/fixer-output.ts +49 -52
- package/src/public-cli/auto-resume.ts +1 -1
- package/src/public-cli/cli.ts +67 -73
- package/src/public-cli/config.ts +8 -0
- package/src/public-cli/instruction-seat-run.ts +82 -55
- package/src/public-cli/invocation.ts +148 -162
- package/src/public-cli/post-admission.ts +110 -100
- package/src/public-cli/role-seat-runtime.ts +72 -0
- package/src/public-cli/run-lifecycle.ts +7 -26
- package/src/public-cli/terminal.ts +2 -17
- package/src/public-cli/turn-request.ts +26 -0
- package/src/public-role-summons.ts +20 -45
- package/src/reviewer-role.ts +16 -35
- package/src/role-run-placement.ts +5 -0
- package/src/secretariat-role.ts +16 -12
- package/src/worker-role.ts +14 -29
- package/src/worker-submission-gates.ts +14 -3
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Seat differences are composition-root fields. An omitted reviewer lens
|
|
4
4
|
* starts two ordinary single-axis runs here.
|
|
5
5
|
*/
|
|
6
|
-
import { dirname, resolve
|
|
6
|
+
import { dirname, resolve } from "node:path";
|
|
7
|
+
import { isUnboundRunDirectory } from "../role-run-placement.js";
|
|
7
8
|
import { AUDITOR_DOSSIER_PROMPT } from "../compliance-transport.js";
|
|
8
9
|
import { bookDirectOfficerRunPointer } from "../archivist-record-pointer.js";
|
|
9
10
|
import { createPiDoctorAuditor } from "../doctor-auditor.js";
|
|
@@ -22,7 +23,7 @@ import { isSafePositiveTicketNumber, readBoardTicketNumber } from "../run-ticket
|
|
|
22
23
|
import { NotarySourceRunError, resolveNotarySourceRunLocator } from "../notary-source-run.js";
|
|
23
24
|
import { engineSessionMaterialFromOptions, pickEngineAxis } from "../package-resources/engine-material.js";
|
|
24
25
|
import { packagedAdmitsCountersign, packagedBindsBoardTicket, packagedRebindSourceOnResume, packagedRoleMetadata, } from "../packaged-role-registry.js";
|
|
25
|
-
import {
|
|
26
|
+
import { readAuditorResumeBinding } from "../auditor-soul.js";
|
|
26
27
|
import { CliUsageError } from "./cli-errors.js";
|
|
27
28
|
import { admitPublicRole, bindAdmittedTicketNumber, buildInstructionTransportPrompt, materializeCountersignInvocation, persistAdmittedSourceRunPath, recordAdmittedCorrelation, recordChildDiaristRun, relocateAdmittedRunToTicket, withPreparedAttachments, } from "./invocation.js";
|
|
28
29
|
import { presentControlledFailure, prepareSummonsResumeMaterials, roleTurnOptions, runPostAdmissionOneShot, runPostAdmissionResumable, runPostAdmissionSeatResume, showResumeErrorPointer, resumeTurnRequestProjectionOptions, } from "./post-admission.js";
|
|
@@ -31,7 +32,7 @@ import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.js";
|
|
|
31
32
|
import { presentStructuralRejection, readBoundSessionEntries, seatKnownFailureResolver, attachPostAuditProjection, trySettlePublicSeat, } from "./settlement.js";
|
|
32
33
|
import { warnMissingMethodSkills } from "./machine-method-skills.js";
|
|
33
34
|
import { formatTerminalResult, isLawfulTypedTerminalOutcome, } from "./terminal.js";
|
|
34
|
-
import { admittedSeatTurnDetails, projectRoleTurnRequest, requiredMethodSkills, } from "./turn-request.js";
|
|
35
|
+
import { admittedSeatTurnDetails, projectPublicTurnAxes, projectRoleTurnRequest, requiredMethodSkills, } from "./turn-request.js";
|
|
35
36
|
import { invokeCourtDiarist, latestPayloadEscalated, } from "./countersign-run.js";
|
|
36
37
|
const AUDITED_ROLES = new Set(["judge", "fixer", "coder", "secretariat", "countersign", "doctor"]);
|
|
37
38
|
const SECRETARIAT_STATUS_REASK = "secretariatStatus 不是 converged、escalate 之一。请重新交卷,secretariatStatus 写明其一。";
|
|
@@ -309,6 +310,29 @@ async function runOmittedLensBatch(argv, parsed, env, io) {
|
|
|
309
310
|
io.stdout(formatTerminalResult(terminal));
|
|
310
311
|
return { exitCode: failed ? 1 : 0, terminal };
|
|
311
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* One same-parent lookup. Seat branches still build their own summons.
|
|
315
|
+
* A found child resumes that child; no prior run returns undefined so the caller mints.
|
|
316
|
+
*/
|
|
317
|
+
function resumeSameParentInstructionSeat(input) {
|
|
318
|
+
return tryResumeSameTicketSeatRun({
|
|
319
|
+
home: input.env.home,
|
|
320
|
+
projectRoot: input.projectRoot,
|
|
321
|
+
role: input.role,
|
|
322
|
+
parentRunPath: input.parentRunPath,
|
|
323
|
+
...(input.ticketNumber === undefined ? {} : { ticketNumber: input.ticketNumber }),
|
|
324
|
+
freshSummons: input.env.freshSummons,
|
|
325
|
+
summons: input.summons,
|
|
326
|
+
resume: (runId, materials) => runPublicInstructionSeatResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, input.env, input.io),
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/** Re-ask words when the parent supplied them; otherwise the caller's own instruction. */
|
|
330
|
+
function sameParentInstruction(env, fallback) {
|
|
331
|
+
const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
|
|
332
|
+
if (resumeInstruction === undefined)
|
|
333
|
+
return fallback;
|
|
334
|
+
return { instruction: resumeInstruction, instructionEmpty: false };
|
|
335
|
+
}
|
|
312
336
|
/**
|
|
313
337
|
* Countersign on the shared entry: gate parent resume, deferred materialization,
|
|
314
338
|
* then the same one-shot settlement as every other seat.
|
|
@@ -319,19 +343,20 @@ async function runCountersignBody(parsed, env, io) {
|
|
|
319
343
|
: undefined;
|
|
320
344
|
if (gateParentRunPath !== undefined) {
|
|
321
345
|
const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction ?? parsed.instruction ?? "";
|
|
322
|
-
const resumed = await
|
|
323
|
-
|
|
346
|
+
const resumed = await resumeSameParentInstructionSeat({
|
|
347
|
+
env,
|
|
348
|
+
io,
|
|
324
349
|
projectRoot: resolve(parsed.project ?? env.cwd),
|
|
325
350
|
role: "countersign",
|
|
326
351
|
parentRunPath: gateParentRunPath,
|
|
327
|
-
...(isSafePositiveTicketNumber(env.boundTicketNumber)
|
|
328
|
-
|
|
352
|
+
...(isSafePositiveTicketNumber(env.boundTicketNumber)
|
|
353
|
+
? { ticketNumber: env.boundTicketNumber }
|
|
354
|
+
: {}),
|
|
329
355
|
summons: {
|
|
330
356
|
sourceRunPath: gateParentRunPath,
|
|
331
357
|
instruction: resumeInstruction,
|
|
332
358
|
instructionEmpty: resumeInstruction.trim() === "",
|
|
333
359
|
},
|
|
334
|
-
resume: (runId, materials) => runPublicInstructionSeatResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, env, io),
|
|
335
360
|
});
|
|
336
361
|
if (resumed != null)
|
|
337
362
|
return resumed;
|
|
@@ -364,12 +389,7 @@ async function runCountersignBody(parsed, env, io) {
|
|
|
364
389
|
admitted = { ...admitted, sourceRunPath: gateParentRunPath };
|
|
365
390
|
}
|
|
366
391
|
const turnProjection = {
|
|
367
|
-
|
|
368
|
-
home: env.home,
|
|
369
|
-
agentDir: env.agentDir,
|
|
370
|
-
...(env.model === undefined ? {} : { model: env.model }),
|
|
371
|
-
...pickEngineAxis(env),
|
|
372
|
-
...(env.timeoutMs === undefined ? {} : { timeoutMs: env.timeoutMs }),
|
|
392
|
+
...projectPublicTurnAxes(env),
|
|
373
393
|
...(env.correlationId === undefined || env.correlationId.trim() === ""
|
|
374
394
|
? {}
|
|
375
395
|
: { correlationId: env.correlationId }),
|
|
@@ -446,17 +466,18 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
446
466
|
let auditorSubject;
|
|
447
467
|
let auditorSource;
|
|
448
468
|
let auditorTicket;
|
|
469
|
+
let resolvedNotarySource;
|
|
449
470
|
if (record.sameParent === "subject-source") {
|
|
450
|
-
if (
|
|
471
|
+
if (parsed.subject === undefined) {
|
|
451
472
|
presentStructuralRejection(new CliUsageError("auditor --subject requires judge|doctor"), io);
|
|
452
473
|
return { exitCode: 2 };
|
|
453
474
|
}
|
|
454
475
|
auditorSubject = parsed.subject;
|
|
455
|
-
|
|
456
|
-
if (source === "") {
|
|
476
|
+
if (parsed.sourceRun === undefined) {
|
|
457
477
|
presentStructuralRejection(new CliUsageError("auditor --source-run requires a run locator"), io);
|
|
458
478
|
return { exitCode: 2 };
|
|
459
479
|
}
|
|
480
|
+
const source = parsed.sourceRun;
|
|
460
481
|
try {
|
|
461
482
|
const resolved = await resolveNotarySourceRunLocator({ projectRoot, sourceRun: source, home: env.home });
|
|
462
483
|
auditorSource = resolved.runDirectory;
|
|
@@ -469,24 +490,23 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
469
490
|
if (auditorSource === undefined)
|
|
470
491
|
return { exitCode: 2 };
|
|
471
492
|
const sourceDirectory = auditorSource;
|
|
472
|
-
const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
|
|
473
493
|
const summons = {
|
|
474
|
-
...(
|
|
475
|
-
|
|
476
|
-
:
|
|
494
|
+
...sameParentInstruction(env, {
|
|
495
|
+
instruction: parsed.instruction ?? "",
|
|
496
|
+
instructionEmpty: (parsed.instruction ?? "").trim() === "",
|
|
497
|
+
}),
|
|
477
498
|
attachmentPaths: parsed.attachmentPaths ?? [],
|
|
478
499
|
};
|
|
479
500
|
const resumed = await withAuditorSoulEnv({
|
|
480
501
|
subject: auditorSubject,
|
|
481
502
|
sourceRunDirectory: auditorSource,
|
|
482
|
-
run: () =>
|
|
483
|
-
|
|
503
|
+
run: () => resumeSameParentInstructionSeat({
|
|
504
|
+
env,
|
|
505
|
+
io,
|
|
484
506
|
projectRoot,
|
|
485
507
|
role,
|
|
486
508
|
parentRunPath: sourceDirectory,
|
|
487
|
-
freshSummons: env.freshSummons,
|
|
488
509
|
summons,
|
|
489
|
-
resume: (runId, materials) => runPublicInstructionSeatResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, env, io),
|
|
490
510
|
}),
|
|
491
511
|
});
|
|
492
512
|
if (resumed != null)
|
|
@@ -495,22 +515,21 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
495
515
|
if (record.sameParent === "gate-pointer") {
|
|
496
516
|
const parentRunPath = parentRunPathFromGatePointerInstruction(parsed.instruction ?? "");
|
|
497
517
|
if (parentRunPath !== undefined) {
|
|
498
|
-
const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
|
|
499
518
|
const summons = {
|
|
500
519
|
sourceRunPath: parentRunPath,
|
|
501
|
-
...(
|
|
502
|
-
|
|
503
|
-
:
|
|
520
|
+
...sameParentInstruction(env, {
|
|
521
|
+
instruction: parsed.instruction ?? "",
|
|
522
|
+
instructionEmpty: (parsed.instruction ?? "").trim() === "",
|
|
523
|
+
}),
|
|
504
524
|
attachmentPaths: parsed.attachmentPaths ?? [],
|
|
505
525
|
};
|
|
506
|
-
const resumed = await
|
|
507
|
-
|
|
526
|
+
const resumed = await resumeSameParentInstructionSeat({
|
|
527
|
+
env,
|
|
528
|
+
io,
|
|
508
529
|
projectRoot,
|
|
509
530
|
role,
|
|
510
531
|
parentRunPath,
|
|
511
|
-
freshSummons: env.freshSummons,
|
|
512
532
|
summons,
|
|
513
|
-
resume: (runId, materials) => runPublicInstructionSeatResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, env, io),
|
|
514
533
|
});
|
|
515
534
|
if (resumed != null)
|
|
516
535
|
return resumed;
|
|
@@ -536,23 +555,29 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
536
555
|
const summons = {
|
|
537
556
|
sourceRunPath: source.runDirectory,
|
|
538
557
|
sourceRun: source,
|
|
539
|
-
...(resumeInstruction === undefined
|
|
558
|
+
...(resumeInstruction === undefined
|
|
559
|
+
? {}
|
|
560
|
+
: { instruction: resumeInstruction, instructionEmpty: false }),
|
|
540
561
|
};
|
|
541
|
-
|
|
542
|
-
|
|
562
|
+
resolvedNotarySource = source;
|
|
563
|
+
const resumed = await resumeSameParentInstructionSeat({
|
|
564
|
+
env,
|
|
565
|
+
io,
|
|
543
566
|
projectRoot,
|
|
544
567
|
role,
|
|
545
568
|
parentRunPath: source.runDirectory,
|
|
546
|
-
freshSummons: env.freshSummons,
|
|
547
569
|
summons,
|
|
548
|
-
resume: (runId, materials) => runPublicInstructionSeatResume({ runId, ...(materials === undefined ? {} : { summons: materials }) }, env, io),
|
|
549
570
|
});
|
|
550
571
|
if (resumed != null)
|
|
551
572
|
return resumed;
|
|
552
573
|
}
|
|
553
574
|
let admitted;
|
|
554
575
|
try {
|
|
555
|
-
|
|
576
|
+
const admissionOverride = {
|
|
577
|
+
...(auditorTicket === undefined ? {} : { assertedTicketNumber: auditorTicket }),
|
|
578
|
+
...(resolvedNotarySource === undefined ? {} : { resolvedSourceRun: resolvedNotarySource }),
|
|
579
|
+
};
|
|
580
|
+
admitted = await admitPublicRole(role, parsed, env, auditorTicket === undefined && resolvedNotarySource === undefined ? undefined : admissionOverride);
|
|
556
581
|
}
|
|
557
582
|
catch (error) {
|
|
558
583
|
const rejected = usageExit(error, io);
|
|
@@ -589,7 +614,7 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
589
614
|
timedOut: false, code: null, stderr: "", thrown: error,
|
|
590
615
|
}, seatAdapters(admitted, env), env.principalAuthority, io);
|
|
591
616
|
}
|
|
592
|
-
if (outcome.admitted
|
|
617
|
+
if (outcome.admitted !== undefined && isUnboundRunDirectory(outcome.admitted.runDirectory)) {
|
|
593
618
|
await recordChildDiaristRun(admitted, outcome.admitted.runId);
|
|
594
619
|
}
|
|
595
620
|
if (outcome.failedWithoutEscalate !== undefined) {
|
|
@@ -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, listBookRunDirectories, roleRunArtifactsDirectory, roleRunPlacement, } from "../role-run-placement.js";
|
|
12
|
+
import { ensureRoleRunDirectory, ensureRoleRunPlacement, isUnboundRunDirectory, 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";
|
|
@@ -103,44 +103,53 @@ async function writeRoleInvocationLedger(source, role, effectiveModel) {
|
|
|
103
103
|
* Host stays write-if-present (`string` only).
|
|
104
104
|
*/
|
|
105
105
|
export async function recordEffectiveInvocationModel(runDirectory, model, engine, host, engineModel) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
await updateDurableJsonPage(join(runDirectory, "invocation.json"), (current) => {
|
|
107
|
+
const next = { ...current };
|
|
108
|
+
if (model !== undefined) {
|
|
109
|
+
next.provider = model.provider;
|
|
110
|
+
next.model = model.model;
|
|
111
|
+
if (model.thinking === undefined) {
|
|
112
|
+
delete next.thinking;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
next.thinking = model.thinking;
|
|
116
|
+
}
|
|
114
117
|
}
|
|
115
|
-
|
|
116
|
-
next.
|
|
118
|
+
if (engine === null) {
|
|
119
|
+
delete next.engine;
|
|
117
120
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
121
|
+
else if (engine !== undefined) {
|
|
122
|
+
next.engine = engine;
|
|
123
|
+
}
|
|
124
|
+
if (engineModel === null) {
|
|
125
|
+
delete next.engineModel;
|
|
126
|
+
}
|
|
127
|
+
else if (engineModel !== undefined) {
|
|
128
|
+
next.engineModel = engineModel;
|
|
129
|
+
}
|
|
130
|
+
if (host !== undefined) {
|
|
131
|
+
next.host = host;
|
|
132
|
+
}
|
|
133
|
+
return next;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Read one durable JSON object, let the caller keep its field rules, and write
|
|
138
|
+
* the page back. `undefined` means the caller decided not to change the page.
|
|
139
|
+
*/
|
|
140
|
+
async function updateDurableJsonPage(path, update) {
|
|
141
|
+
const current = JSON.parse(await readFile(path, "utf8"));
|
|
142
|
+
const next = update(current);
|
|
143
|
+
if (next === undefined)
|
|
144
|
+
return;
|
|
145
|
+
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
135
146
|
}
|
|
136
147
|
/** Merge observed launch-time fields into the single existing invocation.json identity page. */
|
|
137
148
|
async function mergeInvocationIdentityPage(runDirectory, fields) {
|
|
138
|
-
|
|
139
|
-
const current = JSON.parse(await readFile(ledgerPath, "utf8"));
|
|
140
|
-
await writeFile(ledgerPath, `${JSON.stringify({
|
|
149
|
+
await updateDurableJsonPage(join(runDirectory, "invocation.json"), (current) => ({
|
|
141
150
|
...current,
|
|
142
151
|
...fields,
|
|
143
|
-
}
|
|
152
|
+
}));
|
|
144
153
|
}
|
|
145
154
|
/**
|
|
146
155
|
* Persist parent --source-run path onto admitted-request for officer resume lookup (#747).
|
|
@@ -150,19 +159,19 @@ export async function persistAdmittedSourceRunPath(admitted, sourceRunPath, audi
|
|
|
150
159
|
if (sourceRunPath.trim() === "") {
|
|
151
160
|
throw new Error("persistAdmittedSourceRunPath requires a non-empty sourceRunPath");
|
|
152
161
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
162
|
+
await updateDurableJsonPage(admitted.admittedRequestPath, (current) => {
|
|
163
|
+
if (typeof current.sourceRunPath === "string" && current.sourceRunPath !== sourceRunPath) {
|
|
164
|
+
throw new Error(`persistAdmittedSourceRunPath refuses to replace ${current.sourceRunPath} with ${sourceRunPath}`);
|
|
165
|
+
}
|
|
166
|
+
const sameSubject = auditorSubject === undefined || current.auditorSubject === auditorSubject;
|
|
167
|
+
if (current.sourceRunPath === sourceRunPath && sameSubject)
|
|
168
|
+
return undefined;
|
|
169
|
+
return {
|
|
170
|
+
...current,
|
|
171
|
+
sourceRunPath,
|
|
172
|
+
...(auditorSubject === undefined ? {} : { auditorSubject }),
|
|
173
|
+
};
|
|
174
|
+
});
|
|
166
175
|
}
|
|
167
176
|
/**
|
|
168
177
|
* Bind a post-admission resolved ticketNumber onto the in-memory admitted
|
|
@@ -177,17 +186,19 @@ export async function bindAdmittedTicketNumber(admitted, ticketNumber) {
|
|
|
177
186
|
}
|
|
178
187
|
/** Persist each direct caller observed while a retained run is resumed. */
|
|
179
188
|
export async function recordAdmittedCorrelation(admitted, correlationId) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
let correlationIds = [];
|
|
190
|
+
await updateDurableJsonPage(admitted.admittedRequestPath, (current) => {
|
|
191
|
+
const prior = [
|
|
192
|
+
...(Array.isArray(current.correlationIds)
|
|
193
|
+
? current.correlationIds.filter((value) => typeof value === "string" && value.trim() !== "")
|
|
194
|
+
: []),
|
|
195
|
+
...(typeof current.correlationId === "string" && current.correlationId.trim() !== ""
|
|
196
|
+
? [current.correlationId]
|
|
197
|
+
: []),
|
|
198
|
+
];
|
|
199
|
+
correlationIds = [...new Set([...prior, correlationId])];
|
|
200
|
+
return { ...current, correlationId, correlationIds };
|
|
201
|
+
});
|
|
191
202
|
await mergeInvocationIdentityPage(admitted.runDirectory, {
|
|
192
203
|
correlationId,
|
|
193
204
|
correlationIds,
|
|
@@ -198,7 +209,7 @@ export async function recordAdmittedCorrelation(admitted, correlationId) {
|
|
|
198
209
|
}
|
|
199
210
|
/** File a role run under its latest typed ticket identity. */
|
|
200
211
|
export async function relocateAdmittedRunToTicket(admitted, authority, heldLease) {
|
|
201
|
-
if (admitted.ticketNumber === undefined || !admitted.runDirectory
|
|
212
|
+
if (admitted.ticketNumber === undefined || !isUnboundRunDirectory(admitted.runDirectory))
|
|
202
213
|
return undefined;
|
|
203
214
|
const oldRunDirectory = admitted.runDirectory;
|
|
204
215
|
const ledgerHome = resolveActivationLedgerHome(homeFromRunDirectory(oldRunDirectory));
|
|
@@ -264,12 +275,13 @@ export async function relocateAdmittedRunToTicket(admitted, authority, heldLease
|
|
|
264
275
|
}
|
|
265
276
|
/** Persist the exact child identity; later parent binding never inspects siblings. */
|
|
266
277
|
export async function recordChildDiaristRun(parent, childRunId) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
278
|
+
await updateDurableJsonPage(parent.admittedRequestPath, (page) => {
|
|
279
|
+
const existing = Array.isArray(page.childDiaristRunIds)
|
|
280
|
+
? page.childDiaristRunIds.filter((runId) => typeof runId === "string")
|
|
281
|
+
: [];
|
|
282
|
+
const childDiaristRunIds = existing.includes(childRunId) ? existing : [...existing, childRunId];
|
|
283
|
+
return { ...page, childDiaristRunIds };
|
|
284
|
+
});
|
|
273
285
|
}
|
|
274
286
|
/**
|
|
275
287
|
* Bind ticket identity onto a run directory's durable pages when the admitted
|
|
@@ -279,10 +291,16 @@ export async function recordChildDiaristRun(parent, childRunId) {
|
|
|
279
291
|
export async function bindTicketNumberOnRunDirectory(runDirectory, ticketNumber) {
|
|
280
292
|
requireSafePositiveTicketNumber(ticketNumber, "bindTicketNumberOnRunDirectory");
|
|
281
293
|
const admittedPath = join(runDirectory, "admitted-request.json");
|
|
282
|
-
|
|
283
|
-
|
|
294
|
+
let unchanged = false;
|
|
295
|
+
await updateDurableJsonPage(admittedPath, (admitted) => {
|
|
296
|
+
if (admitted.ticketNumber === ticketNumber) {
|
|
297
|
+
unchanged = true;
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
return { ...admitted, ticketNumber };
|
|
301
|
+
});
|
|
302
|
+
if (unchanged)
|
|
284
303
|
return;
|
|
285
|
-
await writeFile(admittedPath, `${JSON.stringify({ ...admitted, ticketNumber }, null, 2)}\n`, "utf8");
|
|
286
304
|
await mergeInvocationIdentityPage(runDirectory, { ticketNumber });
|
|
287
305
|
}
|
|
288
306
|
/** Add the identity returned by the production Pi launch seam to its existing ledger page. */
|
|
@@ -528,18 +546,31 @@ function requireOptionPath(flag, value) {
|
|
|
528
546
|
* next option), and a leading `-` (read as the next Skill option). Not a
|
|
529
547
|
* general free-text gate — only the projection admission seam.
|
|
530
548
|
*/
|
|
549
|
+
function skillArgTokenFault(value) {
|
|
550
|
+
if (value === undefined || value.trim() === "")
|
|
551
|
+
return "empty";
|
|
552
|
+
if (/\s/.test(value))
|
|
553
|
+
return "whitespace";
|
|
554
|
+
if (value.startsWith("-"))
|
|
555
|
+
return "optionLike";
|
|
556
|
+
return undefined;
|
|
557
|
+
}
|
|
531
558
|
function requireSkillArgToken(value, messages) {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (/\s/.test(value)) {
|
|
536
|
-
throw new CliUsageError(messages.whitespace);
|
|
537
|
-
}
|
|
538
|
-
if (value.startsWith("-")) {
|
|
539
|
-
throw new CliUsageError(messages.optionLike);
|
|
540
|
-
}
|
|
559
|
+
const fault = skillArgTokenFault(value);
|
|
560
|
+
if (fault !== undefined)
|
|
561
|
+
throw new CliUsageError(messages[fault]);
|
|
541
562
|
return value;
|
|
542
563
|
}
|
|
564
|
+
/**
|
|
565
|
+
* Durable reviewer base on resume. Same token rule as fresh `--base`,
|
|
566
|
+
* with the resume-owned missing/damaged split.
|
|
567
|
+
*/
|
|
568
|
+
export function admittedReviewerBaseFault(value) {
|
|
569
|
+
const fault = skillArgTokenFault(value);
|
|
570
|
+
if (fault === undefined)
|
|
571
|
+
return undefined;
|
|
572
|
+
return fault === "empty" ? "missing" : "damaged";
|
|
573
|
+
}
|
|
543
574
|
/**
|
|
544
575
|
* Reviewer --base admission: nonempty single Skill-arg token (shared rule with
|
|
545
576
|
* requireAuthorityRef). Multi-token / option-like values smuggle extra flags
|
|
@@ -789,9 +820,6 @@ export async function admitPublicRole(role, parsed, env, override) {
|
|
|
789
820
|
});
|
|
790
821
|
}
|
|
791
822
|
case "worker-packet": {
|
|
792
|
-
if (parsed.project !== undefined) {
|
|
793
|
-
requireOptionPath("--project", parsed.project);
|
|
794
|
-
}
|
|
795
823
|
if (instruction.trim() === "") {
|
|
796
824
|
throw new CliUsageError("fixer requires a nonblank repair instruction");
|
|
797
825
|
}
|
|
@@ -844,19 +872,7 @@ export async function admitPublicRole(role, parsed, env, override) {
|
|
|
844
872
|
});
|
|
845
873
|
}
|
|
846
874
|
case "collect-target": {
|
|
847
|
-
|
|
848
|
-
requireOptionPath("--project", parsed.project);
|
|
849
|
-
}
|
|
850
|
-
let explicitPrNumber;
|
|
851
|
-
if (parsed.prNumber !== undefined) {
|
|
852
|
-
try {
|
|
853
|
-
explicitPrNumber = parseCollectorPrNumber(parsed.prNumber);
|
|
854
|
-
}
|
|
855
|
-
catch (error) {
|
|
856
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
857
|
-
throw new CliUsageError(detail, { cause: error });
|
|
858
|
-
}
|
|
859
|
-
}
|
|
875
|
+
const explicitPrNumber = parsed.prNumber;
|
|
860
876
|
const projectRoot = resolve(parsed.project ?? shared.cwd);
|
|
861
877
|
let repository;
|
|
862
878
|
if (parsed.repo !== undefined) {
|
|
@@ -907,15 +923,10 @@ export async function admitPublicRole(role, parsed, env, override) {
|
|
|
907
923
|
return { ...admittedCollector, repository };
|
|
908
924
|
}
|
|
909
925
|
case "case-identity": {
|
|
910
|
-
if (parsed.
|
|
911
|
-
|
|
912
|
-
}
|
|
913
|
-
const issueNumber = parsed.issueNumber ?? Number.NaN;
|
|
914
|
-
if (!Number.isInteger(issueNumber) ||
|
|
915
|
-
issueNumber < 1 ||
|
|
916
|
-
!DOCTOR_ISSUE_NUMBER_PATTERN.test(String(issueNumber))) {
|
|
917
|
-
throw new CliUsageError(`doctor --issue must be a positive integer, got ${issueNumber}`);
|
|
926
|
+
if (parsed.issueNumber === undefined) {
|
|
927
|
+
throw new CliUsageError(`doctor --issue must be a positive integer, got ${Number.NaN}`);
|
|
918
928
|
}
|
|
929
|
+
const issueNumber = parsed.issueNumber;
|
|
919
930
|
let frozenAttachments = [];
|
|
920
931
|
const admittedDoctor = await admitStandardMaterialInvocation("doctor", {
|
|
921
932
|
...shared,
|
|
@@ -970,23 +981,22 @@ export async function admitPublicRole(role, parsed, env, override) {
|
|
|
970
981
|
return { ...admittedDoctor, attachments: frozenAttachments };
|
|
971
982
|
}
|
|
972
983
|
case "source-locator": {
|
|
973
|
-
if (parsed.project !== undefined) {
|
|
974
|
-
requireOptionPath("--project", parsed.project);
|
|
975
|
-
}
|
|
976
984
|
const projectRoot = resolve(parsed.project ?? shared.cwd);
|
|
977
|
-
let sourceRun;
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
985
|
+
let sourceRun = override?.resolvedSourceRun;
|
|
986
|
+
if (sourceRun === undefined) {
|
|
987
|
+
try {
|
|
988
|
+
sourceRun = await resolveNotarySourceRunLocator({
|
|
989
|
+
projectRoot,
|
|
990
|
+
sourceRun: parsed.sourceRun ?? "",
|
|
991
|
+
home: shared.home,
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
catch (error) {
|
|
995
|
+
if (error instanceof NotarySourceRunError) {
|
|
996
|
+
throw new CliUsageError(error.message, { cause: error });
|
|
997
|
+
}
|
|
998
|
+
throw error;
|
|
988
999
|
}
|
|
989
|
-
throw error;
|
|
990
1000
|
}
|
|
991
1001
|
// Board ticket on the source run wins; otherwise the summons ticket. Code does not infer one.
|
|
992
1002
|
const inheritedTicketNumber = await readBoardTicketNumber(sourceRun.runDirectory);
|
|
@@ -1007,9 +1017,6 @@ export async function admitPublicRole(role, parsed, env, override) {
|
|
|
1007
1017
|
});
|
|
1008
1018
|
}
|
|
1009
1019
|
case "gleaner": {
|
|
1010
|
-
if (parsed.project !== undefined) {
|
|
1011
|
-
requireOptionPath("--project", parsed.project);
|
|
1012
|
-
}
|
|
1013
1020
|
const baseRevision = parsed.baseRevision ?? "";
|
|
1014
1021
|
if (baseRevision.trim() === "") {
|
|
1015
1022
|
throw new CliUsageError("--base requires a nonempty revision");
|
|
@@ -1024,16 +1031,19 @@ export async function admitPublicRole(role, parsed, env, override) {
|
|
|
1024
1031
|
});
|
|
1025
1032
|
}
|
|
1026
1033
|
case "review-basis": {
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1034
|
+
if (parsed.lens === undefined) {
|
|
1035
|
+
throw new CliUsageError("--lens requires completeness or correctness");
|
|
1036
|
+
}
|
|
1037
|
+
if (parsed.baseRevision === undefined) {
|
|
1038
|
+
throw new CliUsageError("--base requires a nonempty revision");
|
|
1030
1039
|
}
|
|
1031
|
-
const baseRevision = requireReviewerBaseRevision(parsed.baseRevision);
|
|
1032
1040
|
const rawRefs = parsed.authorityRefs ?? [];
|
|
1033
1041
|
if (rawRefs.length === 0) {
|
|
1034
1042
|
throw new CliUsageError("reviewer requires --authority-ref <ref>");
|
|
1035
1043
|
}
|
|
1036
|
-
const
|
|
1044
|
+
const lens = parsed.lens;
|
|
1045
|
+
const baseRevision = parsed.baseRevision;
|
|
1046
|
+
const authorityRefs = Object.freeze([...rawRefs]);
|
|
1037
1047
|
const admittedReviewer = await admitStandardMaterialInvocation("reviewer", {
|
|
1038
1048
|
...shared,
|
|
1039
1049
|
instruction,
|
|
@@ -1048,9 +1058,6 @@ export async function admitPublicRole(role, parsed, env, override) {
|
|
|
1048
1058
|
return { ...admittedReviewer, authorityRefs };
|
|
1049
1059
|
}
|
|
1050
1060
|
case "merge-envelope": {
|
|
1051
|
-
if (parsed.project !== undefined) {
|
|
1052
|
-
requireOptionPath("--project", parsed.project);
|
|
1053
|
-
}
|
|
1054
1061
|
if (instruction.trim() === "") {
|
|
1055
1062
|
throw new CliUsageError("merger requires a nonblank task instruction");
|
|
1056
1063
|
}
|
|
@@ -1106,9 +1113,6 @@ function admissionSubject(ticketNumber) {
|
|
|
1106
1113
|
* Every public seat persists through this function. Seat-only fields are supplied by the caller.
|
|
1107
1114
|
*/
|
|
1108
1115
|
async function placeRoleAdmission(options) {
|
|
1109
|
-
if (options.project !== undefined) {
|
|
1110
|
-
requireOptionPath("--project", options.project);
|
|
1111
|
-
}
|
|
1112
1116
|
const projectRoot = resolve(options.project ?? options.cwd);
|
|
1113
1117
|
const runId = options.runId ?? (options.createRunId ?? uuidv7)();
|
|
1114
1118
|
const ticketFields = ticketAdmissionFields(options.assertedTicketNumber);
|