@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
|
@@ -27,6 +27,7 @@ import { resolveBookKeyFromGit } from "../activation-ledger-git.ts";
|
|
|
27
27
|
import {
|
|
28
28
|
ensureRoleRunDirectory,
|
|
29
29
|
ensureRoleRunPlacement,
|
|
30
|
+
isUnboundRunDirectory,
|
|
30
31
|
listBookRunDirectories,
|
|
31
32
|
roleRunArtifactsDirectory,
|
|
32
33
|
roleRunPlacement,
|
|
@@ -36,7 +37,7 @@ import type {
|
|
|
36
37
|
DurablePrincipal,
|
|
37
38
|
DurablePrincipalAuthority,
|
|
38
39
|
} from "../host-contracts.ts";
|
|
39
|
-
import type { PackagedRole } from "../packaged-role-registry.ts";
|
|
40
|
+
import type { PackagedRole, PublicRoleRecord } from "../packaged-role-registry.ts";
|
|
40
41
|
import {
|
|
41
42
|
packagedAdmittedSubject,
|
|
42
43
|
packagedArgvResult,
|
|
@@ -445,39 +446,46 @@ export async function recordEffectiveInvocationModel(
|
|
|
445
446
|
host?: string,
|
|
446
447
|
engineModel?: string | null,
|
|
447
448
|
): Promise<void> {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
delete next.thinking;
|
|
459
|
-
} else {
|
|
460
|
-
next.thinking = model.thinking;
|
|
449
|
+
await updateDurableJsonPage(join(runDirectory, "invocation.json"), (current) => {
|
|
450
|
+
const next: Record<string, unknown> = { ...current };
|
|
451
|
+
if (model !== undefined) {
|
|
452
|
+
next.provider = model.provider;
|
|
453
|
+
next.model = model.model;
|
|
454
|
+
if (model.thinking === undefined) {
|
|
455
|
+
delete next.thinking;
|
|
456
|
+
} else {
|
|
457
|
+
next.thinking = model.thinking;
|
|
458
|
+
}
|
|
461
459
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
460
|
+
if (engine === null) {
|
|
461
|
+
delete next.engine;
|
|
462
|
+
} else if (engine !== undefined) {
|
|
463
|
+
next.engine = engine;
|
|
464
|
+
}
|
|
465
|
+
if (engineModel === null) {
|
|
466
|
+
delete next.engineModel;
|
|
467
|
+
} else if (engineModel !== undefined) {
|
|
468
|
+
next.engineModel = engineModel;
|
|
469
|
+
}
|
|
470
|
+
if (host !== undefined) {
|
|
471
|
+
next.host = host;
|
|
472
|
+
}
|
|
473
|
+
return next;
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Read one durable JSON object, let the caller keep its field rules, and write
|
|
479
|
+
* the page back. `undefined` means the caller decided not to change the page.
|
|
480
|
+
*/
|
|
481
|
+
async function updateDurableJsonPage(
|
|
482
|
+
path: string,
|
|
483
|
+
update: (current: Record<string, unknown>) => Record<string, unknown> | undefined,
|
|
484
|
+
): Promise<void> {
|
|
485
|
+
const current = JSON.parse(await readFile(path, "utf8")) as Record<string, unknown>;
|
|
486
|
+
const next = update(current);
|
|
487
|
+
if (next === undefined) return;
|
|
488
|
+
await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
481
489
|
}
|
|
482
490
|
|
|
483
491
|
/** Merge observed launch-time fields into the single existing invocation.json identity page. */
|
|
@@ -485,16 +493,10 @@ async function mergeInvocationIdentityPage(
|
|
|
485
493
|
runDirectory: string,
|
|
486
494
|
fields: Record<string, unknown>,
|
|
487
495
|
): Promise<void> {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
`${JSON.stringify({
|
|
493
|
-
...current,
|
|
494
|
-
...fields,
|
|
495
|
-
}, null, 2)}\n`,
|
|
496
|
-
"utf8",
|
|
497
|
-
);
|
|
496
|
+
await updateDurableJsonPage(join(runDirectory, "invocation.json"), (current) => ({
|
|
497
|
+
...current,
|
|
498
|
+
...fields,
|
|
499
|
+
}));
|
|
498
500
|
}
|
|
499
501
|
|
|
500
502
|
/**
|
|
@@ -509,27 +511,20 @@ export async function persistAdmittedSourceRunPath(
|
|
|
509
511
|
if (sourceRunPath.trim() === "") {
|
|
510
512
|
throw new Error("persistAdmittedSourceRunPath requires a non-empty sourceRunPath");
|
|
511
513
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
const sameSubject = auditorSubject === undefined || current.auditorSubject === auditorSubject;
|
|
523
|
-
if (current.sourceRunPath === sourceRunPath && sameSubject) return;
|
|
524
|
-
await writeFile(
|
|
525
|
-
admittedPath,
|
|
526
|
-
`${JSON.stringify({
|
|
514
|
+
await updateDurableJsonPage(admitted.admittedRequestPath, (current) => {
|
|
515
|
+
if (typeof current.sourceRunPath === "string" && current.sourceRunPath !== sourceRunPath) {
|
|
516
|
+
throw new Error(
|
|
517
|
+
`persistAdmittedSourceRunPath refuses to replace ${current.sourceRunPath} with ${sourceRunPath}`,
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
const sameSubject = auditorSubject === undefined || current.auditorSubject === auditorSubject;
|
|
521
|
+
if (current.sourceRunPath === sourceRunPath && sameSubject) return undefined;
|
|
522
|
+
return {
|
|
527
523
|
...current,
|
|
528
524
|
sourceRunPath,
|
|
529
525
|
...(auditorSubject === undefined ? {} : { auditorSubject }),
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
);
|
|
526
|
+
};
|
|
527
|
+
});
|
|
533
528
|
}
|
|
534
529
|
|
|
535
530
|
/**
|
|
@@ -551,25 +546,21 @@ export async function recordAdmittedCorrelation(
|
|
|
551
546
|
admitted: AdmittedRoleInvocation,
|
|
552
547
|
correlationId: string,
|
|
553
548
|
): Promise<void> {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
)
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
admitted.admittedRequestPath,
|
|
570
|
-
`${JSON.stringify({ ...current, correlationId, correlationIds }, null, 2)}\n`,
|
|
571
|
-
"utf8",
|
|
572
|
-
);
|
|
549
|
+
let correlationIds: string[] = [];
|
|
550
|
+
await updateDurableJsonPage(admitted.admittedRequestPath, (current) => {
|
|
551
|
+
const prior = [
|
|
552
|
+
...(Array.isArray(current.correlationIds)
|
|
553
|
+
? current.correlationIds.filter((value): value is string =>
|
|
554
|
+
typeof value === "string" && value.trim() !== ""
|
|
555
|
+
)
|
|
556
|
+
: []),
|
|
557
|
+
...(typeof current.correlationId === "string" && current.correlationId.trim() !== ""
|
|
558
|
+
? [current.correlationId]
|
|
559
|
+
: []),
|
|
560
|
+
];
|
|
561
|
+
correlationIds = [...new Set([...prior, correlationId])];
|
|
562
|
+
return { ...current, correlationId, correlationIds };
|
|
563
|
+
});
|
|
573
564
|
await mergeInvocationIdentityPage(admitted.runDirectory, {
|
|
574
565
|
correlationId,
|
|
575
566
|
correlationIds,
|
|
@@ -588,7 +579,7 @@ export async function relocateAdmittedRunToTicket(
|
|
|
588
579
|
authority: DurablePrincipalAuthority,
|
|
589
580
|
heldLease?: { relocate(runDirectory: string): void },
|
|
590
581
|
): Promise<RunDirectoryRelocation | undefined> {
|
|
591
|
-
if (admitted.ticketNumber === undefined || !admitted.runDirectory
|
|
582
|
+
if (admitted.ticketNumber === undefined || !isUnboundRunDirectory(admitted.runDirectory)) return undefined;
|
|
592
583
|
const oldRunDirectory = admitted.runDirectory;
|
|
593
584
|
const ledgerHome = resolveActivationLedgerHome(homeFromRunDirectory(oldRunDirectory));
|
|
594
585
|
const target = roleRunPlacement(ledgerHome, {
|
|
@@ -670,12 +661,13 @@ export async function recordChildDiaristRun(
|
|
|
670
661
|
parent: AdmittedRoleInvocation,
|
|
671
662
|
childRunId: string,
|
|
672
663
|
): Promise<void> {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
664
|
+
await updateDurableJsonPage(parent.admittedRequestPath, (page) => {
|
|
665
|
+
const existing = Array.isArray(page.childDiaristRunIds)
|
|
666
|
+
? page.childDiaristRunIds.filter((runId): runId is string => typeof runId === "string")
|
|
667
|
+
: [];
|
|
668
|
+
const childDiaristRunIds = existing.includes(childRunId) ? existing : [...existing, childRunId];
|
|
669
|
+
return { ...page, childDiaristRunIds };
|
|
670
|
+
});
|
|
679
671
|
}
|
|
680
672
|
|
|
681
673
|
/**
|
|
@@ -692,16 +684,15 @@ export async function bindTicketNumberOnRunDirectory(
|
|
|
692
684
|
"bindTicketNumberOnRunDirectory",
|
|
693
685
|
);
|
|
694
686
|
const admittedPath = join(runDirectory, "admitted-request.json");
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
);
|
|
687
|
+
let unchanged = false;
|
|
688
|
+
await updateDurableJsonPage(admittedPath, (admitted) => {
|
|
689
|
+
if (admitted.ticketNumber === ticketNumber) {
|
|
690
|
+
unchanged = true;
|
|
691
|
+
return undefined;
|
|
692
|
+
}
|
|
693
|
+
return { ...admitted, ticketNumber };
|
|
694
|
+
});
|
|
695
|
+
if (unchanged) return;
|
|
705
696
|
await mergeInvocationIdentityPage(runDirectory, { ticketNumber });
|
|
706
697
|
}
|
|
707
698
|
|
|
@@ -1097,20 +1088,34 @@ function requireOptionPath(
|
|
|
1097
1088
|
* next option), and a leading `-` (read as the next Skill option). Not a
|
|
1098
1089
|
* general free-text gate — only the projection admission seam.
|
|
1099
1090
|
*/
|
|
1091
|
+
function skillArgTokenFault(
|
|
1092
|
+
value: string | undefined,
|
|
1093
|
+
): "empty" | "whitespace" | "optionLike" | undefined {
|
|
1094
|
+
if (value === undefined || value.trim() === "") return "empty";
|
|
1095
|
+
if (/\s/.test(value)) return "whitespace";
|
|
1096
|
+
if (value.startsWith("-")) return "optionLike";
|
|
1097
|
+
return undefined;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
1100
|
function requireSkillArgToken(
|
|
1101
1101
|
value: string | undefined,
|
|
1102
1102
|
messages: { empty: string; whitespace: string; optionLike: string },
|
|
1103
1103
|
): string {
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1104
|
+
const fault = skillArgTokenFault(value);
|
|
1105
|
+
if (fault !== undefined) throw new CliUsageError(messages[fault]);
|
|
1106
|
+
return value as string;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* Durable reviewer base on resume. Same token rule as fresh `--base`,
|
|
1111
|
+
* with the resume-owned missing/damaged split.
|
|
1112
|
+
*/
|
|
1113
|
+
export function admittedReviewerBaseFault(
|
|
1114
|
+
value: string | undefined,
|
|
1115
|
+
): "missing" | "damaged" | undefined {
|
|
1116
|
+
const fault = skillArgTokenFault(value);
|
|
1117
|
+
if (fault === undefined) return undefined;
|
|
1118
|
+
return fault === "empty" ? "missing" : "damaged";
|
|
1114
1119
|
}
|
|
1115
1120
|
|
|
1116
1121
|
/**
|
|
@@ -1420,6 +1425,11 @@ export type PublicSeatParse = {
|
|
|
1420
1425
|
* Sole admit call. Kind comes from the composition-root record.
|
|
1421
1426
|
* Placement stays ticketAdmissionFields. Seat field checks live in this function only.
|
|
1422
1427
|
*/
|
|
1428
|
+
type RoleForAdmission<K extends PublicRoleRecord["admission"]> = Extract<
|
|
1429
|
+
PublicRoleRecord,
|
|
1430
|
+
{ readonly admission: K }
|
|
1431
|
+
>["role"];
|
|
1432
|
+
|
|
1423
1433
|
export function admitPublicRole<R extends PackagedRole>(
|
|
1424
1434
|
role: R,
|
|
1425
1435
|
parsed: PublicSeatParse,
|
|
@@ -1427,6 +1437,8 @@ export function admitPublicRole<R extends PackagedRole>(
|
|
|
1427
1437
|
override?: {
|
|
1428
1438
|
readonly assertedTicketNumber?: number;
|
|
1429
1439
|
readonly deferPersistence?: boolean;
|
|
1440
|
+
/** Locator already resolved on the new-summon path. Absent callers still resolve. */
|
|
1441
|
+
readonly resolvedSourceRun?: NotarySourceRunLocator;
|
|
1430
1442
|
},
|
|
1431
1443
|
): Promise<Extract<AdmittedRoleInvocation, { readonly role: R }>>;
|
|
1432
1444
|
export async function admitPublicRole(
|
|
@@ -1436,6 +1448,8 @@ export async function admitPublicRole(
|
|
|
1436
1448
|
override?: {
|
|
1437
1449
|
readonly assertedTicketNumber?: number;
|
|
1438
1450
|
readonly deferPersistence?: boolean;
|
|
1451
|
+
/** Locator already resolved on the new-summon path. Absent callers still resolve. */
|
|
1452
|
+
readonly resolvedSourceRun?: NotarySourceRunLocator;
|
|
1439
1453
|
},
|
|
1440
1454
|
): Promise<AdmittedRoleInvocation> {
|
|
1441
1455
|
const record = packagedRoleMetadata(role);
|
|
@@ -1454,7 +1468,7 @@ export async function admitPublicRole(
|
|
|
1454
1468
|
switch (record.admission) {
|
|
1455
1469
|
case "instruction":
|
|
1456
1470
|
return admitStandardMaterialInvocation(
|
|
1457
|
-
role as "
|
|
1471
|
+
role as RoleForAdmission<"instruction">,
|
|
1458
1472
|
{
|
|
1459
1473
|
...shared,
|
|
1460
1474
|
instruction,
|
|
@@ -1493,9 +1507,6 @@ export async function admitPublicRole(
|
|
|
1493
1507
|
});
|
|
1494
1508
|
}
|
|
1495
1509
|
case "worker-packet": {
|
|
1496
|
-
if (parsed.project !== undefined) {
|
|
1497
|
-
requireOptionPath("--project", parsed.project);
|
|
1498
|
-
}
|
|
1499
1510
|
if (instruction.trim() === "") {
|
|
1500
1511
|
throw new CliUsageError("fixer requires a nonblank repair instruction");
|
|
1501
1512
|
}
|
|
@@ -1553,18 +1564,7 @@ export async function admitPublicRole(
|
|
|
1553
1564
|
});
|
|
1554
1565
|
}
|
|
1555
1566
|
case "collect-target": {
|
|
1556
|
-
|
|
1557
|
-
requireOptionPath("--project", parsed.project);
|
|
1558
|
-
}
|
|
1559
|
-
let explicitPrNumber: number | undefined;
|
|
1560
|
-
if (parsed.prNumber !== undefined) {
|
|
1561
|
-
try {
|
|
1562
|
-
explicitPrNumber = parseCollectorPrNumber(parsed.prNumber);
|
|
1563
|
-
} catch (error) {
|
|
1564
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
1565
|
-
throw new CliUsageError(detail, { cause: error });
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1567
|
+
const explicitPrNumber = parsed.prNumber;
|
|
1568
1568
|
const projectRoot = resolve(parsed.project ?? shared.cwd);
|
|
1569
1569
|
let repository: CollectorRepository;
|
|
1570
1570
|
if (parsed.repo !== undefined) {
|
|
@@ -1615,19 +1615,12 @@ export async function admitPublicRole(
|
|
|
1615
1615
|
return { ...admittedCollector, repository };
|
|
1616
1616
|
}
|
|
1617
1617
|
case "case-identity": {
|
|
1618
|
-
if (parsed.
|
|
1619
|
-
requireOptionPath("--project", parsed.project);
|
|
1620
|
-
}
|
|
1621
|
-
const issueNumber = parsed.issueNumber ?? Number.NaN;
|
|
1622
|
-
if (
|
|
1623
|
-
!Number.isInteger(issueNumber) ||
|
|
1624
|
-
issueNumber < 1 ||
|
|
1625
|
-
!DOCTOR_ISSUE_NUMBER_PATTERN.test(String(issueNumber))
|
|
1626
|
-
) {
|
|
1618
|
+
if (parsed.issueNumber === undefined) {
|
|
1627
1619
|
throw new CliUsageError(
|
|
1628
|
-
`doctor --issue must be a positive integer, got ${
|
|
1620
|
+
`doctor --issue must be a positive integer, got ${Number.NaN}`,
|
|
1629
1621
|
);
|
|
1630
1622
|
}
|
|
1623
|
+
const issueNumber = parsed.issueNumber;
|
|
1631
1624
|
let frozenAttachments: readonly FrozenAttachment[] = [];
|
|
1632
1625
|
const admittedDoctor = await admitStandardMaterialInvocation("doctor", {
|
|
1633
1626
|
...shared,
|
|
@@ -1683,22 +1676,21 @@ export async function admitPublicRole(
|
|
|
1683
1676
|
return { ...admittedDoctor, attachments: frozenAttachments };
|
|
1684
1677
|
}
|
|
1685
1678
|
case "source-locator": {
|
|
1686
|
-
if (parsed.project !== undefined) {
|
|
1687
|
-
requireOptionPath("--project", parsed.project);
|
|
1688
|
-
}
|
|
1689
1679
|
const projectRoot = resolve(parsed.project ?? shared.cwd);
|
|
1690
|
-
let sourceRun
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1680
|
+
let sourceRun = override?.resolvedSourceRun;
|
|
1681
|
+
if (sourceRun === undefined) {
|
|
1682
|
+
try {
|
|
1683
|
+
sourceRun = await resolveNotarySourceRunLocator({
|
|
1684
|
+
projectRoot,
|
|
1685
|
+
sourceRun: parsed.sourceRun ?? "",
|
|
1686
|
+
home: shared.home,
|
|
1687
|
+
});
|
|
1688
|
+
} catch (error) {
|
|
1689
|
+
if (error instanceof NotarySourceRunError) {
|
|
1690
|
+
throw new CliUsageError(error.message, { cause: error });
|
|
1691
|
+
}
|
|
1692
|
+
throw error;
|
|
1700
1693
|
}
|
|
1701
|
-
throw error;
|
|
1702
1694
|
}
|
|
1703
1695
|
// Board ticket on the source run wins; otherwise the summons ticket. Code does not infer one.
|
|
1704
1696
|
const inheritedTicketNumber = await readBoardTicketNumber(sourceRun.runDirectory);
|
|
@@ -1719,9 +1711,6 @@ export async function admitPublicRole(
|
|
|
1719
1711
|
});
|
|
1720
1712
|
}
|
|
1721
1713
|
case "gleaner": {
|
|
1722
|
-
if (parsed.project !== undefined) {
|
|
1723
|
-
requireOptionPath("--project", parsed.project);
|
|
1724
|
-
}
|
|
1725
1714
|
const baseRevision = parsed.baseRevision ?? "";
|
|
1726
1715
|
if (baseRevision.trim() === "") {
|
|
1727
1716
|
throw new CliUsageError("--base requires a nonempty revision");
|
|
@@ -1736,16 +1725,19 @@ export async function admitPublicRole(
|
|
|
1736
1725
|
});
|
|
1737
1726
|
}
|
|
1738
1727
|
case "review-basis": {
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1728
|
+
if (parsed.lens === undefined) {
|
|
1729
|
+
throw new CliUsageError("--lens requires completeness or correctness");
|
|
1730
|
+
}
|
|
1731
|
+
if (parsed.baseRevision === undefined) {
|
|
1732
|
+
throw new CliUsageError("--base requires a nonempty revision");
|
|
1742
1733
|
}
|
|
1743
|
-
const baseRevision = requireReviewerBaseRevision(parsed.baseRevision);
|
|
1744
1734
|
const rawRefs = parsed.authorityRefs ?? [];
|
|
1745
1735
|
if (rawRefs.length === 0) {
|
|
1746
1736
|
throw new CliUsageError("reviewer requires --authority-ref <ref>");
|
|
1747
1737
|
}
|
|
1748
|
-
const
|
|
1738
|
+
const lens = parsed.lens;
|
|
1739
|
+
const baseRevision = parsed.baseRevision;
|
|
1740
|
+
const authorityRefs = Object.freeze([...rawRefs]);
|
|
1749
1741
|
const admittedReviewer = await admitStandardMaterialInvocation("reviewer", {
|
|
1750
1742
|
...shared,
|
|
1751
1743
|
instruction,
|
|
@@ -1760,9 +1752,6 @@ export async function admitPublicRole(
|
|
|
1760
1752
|
return { ...admittedReviewer, authorityRefs };
|
|
1761
1753
|
}
|
|
1762
1754
|
case "merge-envelope": {
|
|
1763
|
-
if (parsed.project !== undefined) {
|
|
1764
|
-
requireOptionPath("--project", parsed.project);
|
|
1765
|
-
}
|
|
1766
1755
|
if (instruction.trim() === "") {
|
|
1767
1756
|
throw new CliUsageError("merger requires a nonblank task instruction");
|
|
1768
1757
|
}
|
|
@@ -1885,9 +1874,6 @@ async function placeRoleAdmission(options: {
|
|
|
1885
1874
|
/** Countersign reserves coordinates, then materializes after identity lookup. */
|
|
1886
1875
|
readonly materialize?: boolean;
|
|
1887
1876
|
}): Promise<PlacedRoleAdmission> {
|
|
1888
|
-
if (options.project !== undefined) {
|
|
1889
|
-
requireOptionPath("--project", options.project);
|
|
1890
|
-
}
|
|
1891
1877
|
const projectRoot = resolve(options.project ?? options.cwd);
|
|
1892
1878
|
const runId = options.runId ?? (options.createRunId ?? uuidv7)();
|
|
1893
1879
|
const ticketFields = ticketAdmissionFields(options.assertedTicketNumber);
|
|
@@ -1988,7 +1974,7 @@ async function persistPlacedAdmission(
|
|
|
1988
1974
|
* doctor resolves the case, then freezes attachments; merger writes merger-input.json).
|
|
1989
1975
|
*/
|
|
1990
1976
|
async function admitStandardMaterialInvocation<
|
|
1991
|
-
R extends
|
|
1977
|
+
R extends PackagedRole,
|
|
1992
1978
|
Extra extends object = {},
|
|
1993
1979
|
>(
|
|
1994
1980
|
role: R,
|