@akagilnc/pi-workflow-roles 0.1.4062 → 0.1.4196
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 +2075 -1088
- package/dist/archivist-record-entry.js +204 -20
- package/dist/archivist-record-topology.js +36 -16
- package/dist/auditor-dossier-tool.js +2 -1
- package/dist/diarist-contracts.js +96 -0
- package/dist/doctor-evidence.js +3 -2
- package/dist/headless-host/production-host.js +2215 -1228
- package/dist/migrate-book-topology.js +278917 -0
- package/dist/navigator-public-session.js +22 -25
- package/dist/notary-source-run.js +7 -4
- package/dist/pi/durable-principal.js +9 -6
- package/dist/public-cli/auto-resume.js +2 -5
- package/dist/public-cli/diarist-run.js +27 -25
- package/dist/public-cli/instruction-seat-run.js +8 -11
- package/dist/public-cli/invocation.js +127 -63
- package/dist/public-cli/main.js +1507 -645
- package/dist/public-cli/option-definitions.js +2 -2
- package/dist/public-cli/post-admission.js +47 -12
- package/dist/public-cli/run-lifecycle.js +206 -58
- package/dist/public-cli/seat-ticket-binding.js +3 -6
- package/dist/public-cli/settlement.js +5 -3
- package/dist/role-run-placement.js +84 -0
- package/dist/role-run-relocation.js +358 -0
- package/dist/run-terminal-artifacts.js +2 -1
- package/dist/run-ticket-number.js +51 -32
- package/dist/sitian-appender.js +11 -26
- package/dist/submission-ledger.js +78 -19
- package/dist/ticket-provenance.js +3 -1
- package/package.json +3 -2
- package/scripts/build-package.mjs +34 -2
- package/souls/countersign.md +2 -2
- package/souls/fixer.md +3 -2
- package/src/analyst-ledger.ts +14 -15
- package/src/archivist-record-entry.ts +285 -38
- package/src/archivist-record-topology.ts +43 -18
- package/src/auditor-dossier-tool.ts +2 -1
- package/src/book-topology-migration-placement.ts +493 -0
- package/src/book-topology-migration.ts +220 -0
- package/src/book-topology-migrators-t11.ts +856 -0
- package/src/book-topology-mixed-volume-migrators.ts +470 -0
- package/src/book-topology-partition-migrators.ts +25 -0
- package/src/book-topology-record-class-migrators.ts +779 -0
- package/src/book-topology-runs-migrator.ts +244 -0
- package/src/diarist-contracts.ts +116 -0
- package/src/diarist-role.ts +6 -0
- package/src/diarist.ts +2 -0
- package/src/doctor-evidence.ts +4 -1
- package/src/doctor-role.ts +1 -1
- package/src/navigator-public-session.ts +33 -30
- package/src/notary-source-run.ts +9 -4
- package/src/pi/durable-principal.ts +9 -13
- package/src/public-cli/auto-resume.ts +2 -6
- package/src/public-cli/countersign-run.ts +182 -32
- package/src/public-cli/diarist-run.ts +34 -26
- package/src/public-cli/instruction-seat-run.ts +7 -11
- package/src/public-cli/invocation.ts +192 -58
- package/src/public-cli/option-definitions.ts +2 -2
- package/src/public-cli/post-admission.ts +73 -13
- package/src/public-cli/run-lifecycle.ts +255 -65
- package/src/public-cli/seat-ticket-binding.ts +3 -6
- package/src/public-cli/settlement.ts +6 -4
- package/src/role-run-placement.ts +128 -0
- package/src/role-run-relocation.ts +491 -0
- package/src/role-runtime.ts +11 -5
- package/src/run-terminal-artifacts.ts +3 -1
- package/src/run-ticket-number.ts +80 -20
- package/src/sitian-appender.ts +25 -28
- package/src/submission-ledger.ts +102 -19
- package/src/ticket-provenance.ts +4 -1
- package/src/ticket-trajectory.ts +22 -35
- package/src/worker-role.ts +4 -4
- package/src/worker-submission-gates.ts +6 -6
|
@@ -6,12 +6,12 @@ import { execFileSync } from "node:child_process";
|
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
7
|
import {
|
|
8
8
|
lstat,
|
|
9
|
-
mkdir,
|
|
10
9
|
readFile,
|
|
11
10
|
realpath,
|
|
11
|
+
rename,
|
|
12
12
|
writeFile,
|
|
13
13
|
} from "node:fs/promises";
|
|
14
|
-
import { basename, isAbsolute, join, resolve, sep } from "node:path";
|
|
14
|
+
import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
17
|
activationBookDirectory,
|
|
@@ -21,14 +21,31 @@ import {
|
|
|
21
21
|
resolveActivationLedgerHome,
|
|
22
22
|
} from "../activation-ledger-topology.ts";
|
|
23
23
|
import { resolveBookKeyFromGit } from "../activation-ledger-git.ts";
|
|
24
|
+
import {
|
|
25
|
+
ensureRoleRunDirectory,
|
|
26
|
+
ensureRoleRunPlacement,
|
|
27
|
+
roleRunArtifactsDirectory,
|
|
28
|
+
roleRunPlacement,
|
|
29
|
+
type RoleRunSubject,
|
|
30
|
+
} from "../role-run-placement.ts";
|
|
24
31
|
import type {
|
|
25
32
|
DurablePrincipal,
|
|
26
33
|
DurablePrincipalAuthority,
|
|
27
34
|
} from "../host-contracts.ts";
|
|
28
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
isSafePositiveTicketNumber,
|
|
37
|
+
readBoardTicketNumber,
|
|
38
|
+
requireSafePositiveTicketNumber,
|
|
39
|
+
} from "../run-ticket-number.ts";
|
|
40
|
+
import {
|
|
41
|
+
rewriteRoleRunDurablePages,
|
|
42
|
+
rewriteRunDirectoryPathFields,
|
|
43
|
+
rewriteRunDirectoryPathValue,
|
|
44
|
+
} from "../role-run-relocation.ts";
|
|
29
45
|
import {
|
|
30
46
|
loadDoctorCase,
|
|
31
47
|
} from "../doctor-evidence.ts";
|
|
48
|
+
import { projectCourtTicketNumbers } from "../diarist-contracts.ts";
|
|
32
49
|
import type { DoctorCaseIdentity } from "../doctor-contracts.ts";
|
|
33
50
|
import {
|
|
34
51
|
emptyCollectorManifest,
|
|
@@ -121,6 +138,17 @@ export type AdmittedJudgeInvocation = AdmittedRoleInvocationBase & {
|
|
|
121
138
|
|
|
122
139
|
export type AdmittedCountersignInvocation = AdmittedRoleInvocationBase & {
|
|
123
140
|
readonly role: "countersign";
|
|
141
|
+
/**
|
|
142
|
+
* #871 typed co-review set for this countersign run (durable run fact).
|
|
143
|
+
* Main ticket remains `ticketNumber`; this set drives per-ticket court diarist refresh.
|
|
144
|
+
* Whole-set replace on new typed submission — never union with history.
|
|
145
|
+
*/
|
|
146
|
+
courtTicketNumbers?: readonly number[];
|
|
147
|
+
/**
|
|
148
|
+
* Load-time durable set damage diagnostic (#871 B7). Resume must settle as
|
|
149
|
+
* countersign controlled failure with this text — never structural exit 2 or main-only.
|
|
150
|
+
*/
|
|
151
|
+
courtTicketNumbersDamage?: string;
|
|
124
152
|
};
|
|
125
153
|
|
|
126
154
|
export type AdmittedGleanerLeftInvocation = AdmittedRoleInvocationBase & {
|
|
@@ -261,30 +289,36 @@ export type AdmissionPlacement = {
|
|
|
261
289
|
readonly sessionDirectory: string;
|
|
262
290
|
readonly sessionFile: string;
|
|
263
291
|
readonly runDirectory: string;
|
|
292
|
+
readonly artifactsDirectory: string;
|
|
293
|
+
readonly attachmentsDirectory: string;
|
|
264
294
|
readonly ledgerHome: string;
|
|
265
295
|
readonly bookKey: string;
|
|
266
296
|
};
|
|
267
297
|
|
|
268
|
-
/** Issue principal
|
|
298
|
+
/** Issue a principal from the single authoritative role-run placement. */
|
|
269
299
|
export function issueAdmissionPlacement(
|
|
270
300
|
authority: DurablePrincipalAuthority,
|
|
271
301
|
request: {
|
|
272
302
|
readonly cwd: string;
|
|
273
303
|
readonly runId: string;
|
|
274
304
|
readonly role: AdmittedRoleInvocation["role"];
|
|
305
|
+
/** Identity already asserted by 起居郎; never derive this from CLI parameters. */
|
|
306
|
+
readonly subject: RoleRunSubject;
|
|
275
307
|
readonly home?: string;
|
|
276
308
|
},
|
|
277
309
|
): AdmissionPlacement {
|
|
278
|
-
const principal = authority.issue(request);
|
|
279
|
-
const { sessionDirectory, sessionFile } = authority.decode(principal);
|
|
280
|
-
const runDirectory = join(sessionDirectory, "..");
|
|
281
310
|
const ledgerHome = resolveActivationLedgerHome(request.home);
|
|
282
311
|
const bookKey = resolveBookKeyFromGit(request.cwd);
|
|
312
|
+
const placement = roleRunPlacement(ledgerHome, {
|
|
313
|
+
bookKey,
|
|
314
|
+
subject: request.subject,
|
|
315
|
+
runId: request.runId,
|
|
316
|
+
role: request.role,
|
|
317
|
+
});
|
|
318
|
+
ensureRoleRunPlacement(ledgerHome, placement);
|
|
283
319
|
return {
|
|
284
|
-
principal,
|
|
285
|
-
|
|
286
|
-
sessionFile,
|
|
287
|
-
runDirectory,
|
|
320
|
+
principal: authority.seal(placement),
|
|
321
|
+
...placement,
|
|
288
322
|
ledgerHome,
|
|
289
323
|
bookKey,
|
|
290
324
|
};
|
|
@@ -476,17 +510,50 @@ export async function bindAdmittedTicketNumber(
|
|
|
476
510
|
admitted: AdmittedRoleInvocation,
|
|
477
511
|
ticketNumber: number,
|
|
478
512
|
): Promise<void> {
|
|
479
|
-
if (!Number.isSafeInteger(ticketNumber) || ticketNumber < 1) {
|
|
480
|
-
throw new Error(`bindAdmittedTicketNumber requires a safe positive integer, got ${String(ticketNumber)}`);
|
|
481
|
-
}
|
|
482
513
|
if (admitted.ticketNumber !== undefined) {
|
|
483
514
|
if (admitted.ticketNumber === ticketNumber) return;
|
|
484
515
|
throw new Error(
|
|
485
516
|
`bindAdmittedTicketNumber refuses to replace existing ticket #${admitted.ticketNumber} with #${ticketNumber}`,
|
|
486
517
|
);
|
|
487
518
|
}
|
|
519
|
+
await bindTicketNumberOnRunDirectory(admitted.runDirectory, ticketNumber);
|
|
488
520
|
(admitted as { ticketNumber?: number }).ticketNumber = ticketNumber;
|
|
489
|
-
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* #871: persist the typed co-review set as a countersign run fact (admitted-request +
|
|
525
|
+
* invocation.json). Whole-set replace — never union with a prior set. Main ticket
|
|
526
|
+
* binding stays on `ticketNumber` alone. Projection reuses the sole contract helper;
|
|
527
|
+
* principal membership is mandatory.
|
|
528
|
+
*/
|
|
529
|
+
export async function bindCourtTicketNumbersOnAdmitted(
|
|
530
|
+
admitted: AdmittedCountersignInvocation,
|
|
531
|
+
courtTicketNumbers: readonly number[],
|
|
532
|
+
): Promise<void> {
|
|
533
|
+
if (admitted.ticketNumber === undefined) {
|
|
534
|
+
throw new Error(
|
|
535
|
+
"bindCourtTicketNumbersOnAdmitted requires a bound principal ticketNumber",
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
const principal = requireSafePositiveTicketNumber(
|
|
539
|
+
admitted.ticketNumber,
|
|
540
|
+
"bindCourtTicketNumbersOnAdmitted principal ticketNumber",
|
|
541
|
+
);
|
|
542
|
+
// Strict write path: every member must already be a lawful number (no soft filter).
|
|
543
|
+
for (const item of courtTicketNumbers) {
|
|
544
|
+
if (!isSafePositiveTicketNumber(item)) {
|
|
545
|
+
throw new Error(
|
|
546
|
+
`bindCourtTicketNumbersOnAdmitted requires safe positive integers, got ${String(item)}`,
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
const projected = projectCourtTicketNumbers(courtTicketNumbers, {
|
|
551
|
+
principalTicket: principal,
|
|
552
|
+
});
|
|
553
|
+
if (projected === null || projected.length === 0) {
|
|
554
|
+
throw new Error("bindCourtTicketNumbersOnAdmitted requires a non-empty ticket set");
|
|
555
|
+
}
|
|
556
|
+
const frozen = Object.freeze([...projected]);
|
|
490
557
|
const admittedPath = admitted.admittedRequestPath;
|
|
491
558
|
const current = JSON.parse(await readFile(admittedPath, "utf8")) as Record<
|
|
492
559
|
string,
|
|
@@ -494,9 +561,64 @@ export async function bindAdmittedTicketNumber(
|
|
|
494
561
|
>;
|
|
495
562
|
await writeFile(
|
|
496
563
|
admittedPath,
|
|
497
|
-
`${JSON.stringify({ ...current,
|
|
564
|
+
`${JSON.stringify({ ...current, courtTicketNumbers: frozen }, null, 2)}\n`,
|
|
498
565
|
"utf8",
|
|
499
566
|
);
|
|
567
|
+
await mergeInvocationIdentityPage(admitted.runDirectory, {
|
|
568
|
+
courtTicketNumbers: frozen,
|
|
569
|
+
});
|
|
570
|
+
admitted.courtTicketNumbers = frozen;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/** Move a settled first-entry run from unbound to its asserted ticket directory. */
|
|
574
|
+
export async function relocateAdmittedRunToTicket(
|
|
575
|
+
admitted: AdmittedRoleInvocation,
|
|
576
|
+
authority: DurablePrincipalAuthority,
|
|
577
|
+
heldLease?: { relocate(runDirectory: string): void },
|
|
578
|
+
): Promise<void> {
|
|
579
|
+
if (admitted.ticketNumber === undefined || !admitted.runDirectory.includes(`${sep}unbound${sep}runs${sep}`)) return;
|
|
580
|
+
const oldRunDirectory = admitted.runDirectory;
|
|
581
|
+
const ledgerHome = resolveActivationLedgerHome(homeFromRunDirectory(oldRunDirectory));
|
|
582
|
+
const target = roleRunPlacement(ledgerHome, {
|
|
583
|
+
bookKey: admitted.bookKey,
|
|
584
|
+
subject: { ticketNumber: admitted.ticketNumber },
|
|
585
|
+
runId: admitted.runId,
|
|
586
|
+
role: admitted.role,
|
|
587
|
+
});
|
|
588
|
+
ensureRoleRunDirectory(ledgerHome, dirname(target.runDirectory));
|
|
589
|
+
await rename(oldRunDirectory, target.runDirectory);
|
|
590
|
+
heldLease?.relocate(target.runDirectory);
|
|
591
|
+
|
|
592
|
+
const admittedRecord = admitted as unknown as Record<string, unknown>;
|
|
593
|
+
rewriteRunDirectoryPathFields(
|
|
594
|
+
admittedRecord,
|
|
595
|
+
[
|
|
596
|
+
"runDirectory",
|
|
597
|
+
"admittedRequestPath",
|
|
598
|
+
"taskPath",
|
|
599
|
+
"packetPath",
|
|
600
|
+
"prerequisitesPath",
|
|
601
|
+
"requestManifestPath",
|
|
602
|
+
"mergerInputPath",
|
|
603
|
+
],
|
|
604
|
+
oldRunDirectory,
|
|
605
|
+
target.runDirectory,
|
|
606
|
+
);
|
|
607
|
+
for (const attachment of admitted.attachments) {
|
|
608
|
+
(attachment as { frozenPath: string }).frozenPath = rewriteRunDirectoryPathValue(
|
|
609
|
+
attachment.frozenPath,
|
|
610
|
+
oldRunDirectory,
|
|
611
|
+
target.runDirectory,
|
|
612
|
+
) as string;
|
|
613
|
+
}
|
|
614
|
+
const principal = authority.seal(target);
|
|
615
|
+
(admitted as { principal: DurablePrincipal }).principal = principal;
|
|
616
|
+
|
|
617
|
+
await rewriteRoleRunDurablePages({
|
|
618
|
+
pagesDirectory: target.runDirectory,
|
|
619
|
+
oldRunDirectory,
|
|
620
|
+
newRunDirectory: target.runDirectory,
|
|
621
|
+
});
|
|
500
622
|
}
|
|
501
623
|
|
|
502
624
|
/**
|
|
@@ -508,11 +630,10 @@ export async function bindTicketNumberOnRunDirectory(
|
|
|
508
630
|
runDirectory: string,
|
|
509
631
|
ticketNumber: number,
|
|
510
632
|
): Promise<void> {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
633
|
+
requireSafePositiveTicketNumber(
|
|
634
|
+
ticketNumber,
|
|
635
|
+
"bindTicketNumberOnRunDirectory",
|
|
636
|
+
);
|
|
516
637
|
const admittedPath = join(runDirectory, "admitted-request.json");
|
|
517
638
|
const invocationPath = join(runDirectory, "invocation.json");
|
|
518
639
|
const admitted = JSON.parse(await readFile(admittedPath, "utf8")) as Record<
|
|
@@ -1089,7 +1210,13 @@ export async function freezeAttachmentsIntoRun(
|
|
|
1089
1210
|
function ticketAdmissionFields(
|
|
1090
1211
|
ticketNumber: number | undefined,
|
|
1091
1212
|
): { ticketNumber?: number } {
|
|
1092
|
-
|
|
1213
|
+
if (ticketNumber === undefined) return {};
|
|
1214
|
+
return {
|
|
1215
|
+
ticketNumber: requireSafePositiveTicketNumber(
|
|
1216
|
+
ticketNumber,
|
|
1217
|
+
"assertedTicketNumber",
|
|
1218
|
+
),
|
|
1219
|
+
};
|
|
1093
1220
|
}
|
|
1094
1221
|
|
|
1095
1222
|
export type AdmitJudgeInvocationOptions = {
|
|
@@ -1107,6 +1234,8 @@ export type AdmitJudgeInvocationOptions = {
|
|
|
1107
1234
|
|
|
1108
1235
|
export type AdmitInspectorInvocationOptions = AdmitJudgeInvocationOptions & {
|
|
1109
1236
|
correlationId?: string;
|
|
1237
|
+
/** Typed identity handed off by 起居郎 or inherited from an already-bound run. */
|
|
1238
|
+
assertedTicketNumber?: number;
|
|
1110
1239
|
};
|
|
1111
1240
|
|
|
1112
1241
|
export type AdmitGatekeeperInvocationOptions = AdmitInspectorInvocationOptions;
|
|
@@ -1123,7 +1252,7 @@ async function admitStandardMaterialInvocation<
|
|
|
1123
1252
|
R extends "judge" | "inspector" | "gatekeeper" | "navigator" | "auditor" | "diarist",
|
|
1124
1253
|
>(
|
|
1125
1254
|
role: R,
|
|
1126
|
-
options:
|
|
1255
|
+
options: AdmitInspectorInvocationOptions,
|
|
1127
1256
|
): Promise<AdmittedRoleInvocationBase & { readonly role: R }> {
|
|
1128
1257
|
// Empty project override must not reach resolve("") → cwd (silent default).
|
|
1129
1258
|
if (options.project !== undefined) {
|
|
@@ -1131,22 +1260,25 @@ async function admitStandardMaterialInvocation<
|
|
|
1131
1260
|
}
|
|
1132
1261
|
const projectRoot = resolve(options.project ?? options.cwd);
|
|
1133
1262
|
const runId = (options.createRunId ?? uuidv7)();
|
|
1263
|
+
// Validate asserted ticket before placement so 0/NaN never become subjects.
|
|
1264
|
+
const ticketFields = ticketAdmissionFields(options.assertedTicketNumber);
|
|
1134
1265
|
const {
|
|
1135
1266
|
principal,
|
|
1136
1267
|
sessionDirectory,
|
|
1137
1268
|
sessionFile,
|
|
1138
1269
|
runDirectory,
|
|
1270
|
+
attachmentsDirectory,
|
|
1139
1271
|
ledgerHome,
|
|
1140
1272
|
bookKey,
|
|
1141
1273
|
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
1142
1274
|
cwd: projectRoot,
|
|
1143
1275
|
runId,
|
|
1144
1276
|
role,
|
|
1277
|
+
subject: ticketFields.ticketNumber === undefined
|
|
1278
|
+
? { unbound: true }
|
|
1279
|
+
: { ticketNumber: ticketFields.ticketNumber },
|
|
1145
1280
|
home: options.home,
|
|
1146
1281
|
});
|
|
1147
|
-
const attachmentsDirectory = join(runDirectory, "attachments");
|
|
1148
|
-
ensureRealDirectoryTree(ledgerHome, sessionDirectory);
|
|
1149
|
-
ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
|
|
1150
1282
|
|
|
1151
1283
|
const attachments = await freezeAttachments(options.attachmentPaths, attachmentsDirectory);
|
|
1152
1284
|
const correlationFields =
|
|
@@ -1173,6 +1305,7 @@ async function admitStandardMaterialInvocation<
|
|
|
1173
1305
|
sha256: a.sha256,
|
|
1174
1306
|
mediaKind: a.mediaKind,
|
|
1175
1307
|
})),
|
|
1308
|
+
...ticketFields,
|
|
1176
1309
|
};
|
|
1177
1310
|
const admittedRequestPath = join(runDirectory, "admitted-request.json");
|
|
1178
1311
|
await writeAdmittedRequestPersistence(admittedRequestPath, admitted, {
|
|
@@ -1197,6 +1330,7 @@ async function admitStandardMaterialInvocation<
|
|
|
1197
1330
|
principal,
|
|
1198
1331
|
admittedRequestPath,
|
|
1199
1332
|
...correlationFields,
|
|
1333
|
+
...ticketFields,
|
|
1200
1334
|
};
|
|
1201
1335
|
}
|
|
1202
1336
|
|
|
@@ -1324,17 +1458,16 @@ export async function admitCountersignInvocation(
|
|
|
1324
1458
|
sessionDirectory,
|
|
1325
1459
|
sessionFile,
|
|
1326
1460
|
runDirectory,
|
|
1461
|
+
attachmentsDirectory,
|
|
1327
1462
|
ledgerHome,
|
|
1328
1463
|
bookKey,
|
|
1329
1464
|
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
1330
1465
|
cwd: projectRoot,
|
|
1331
1466
|
runId,
|
|
1332
1467
|
role: "countersign",
|
|
1468
|
+
subject: { unbound: true },
|
|
1333
1469
|
home: options.home,
|
|
1334
1470
|
});
|
|
1335
|
-
const attachmentsDirectory = join(runDirectory, "attachments");
|
|
1336
|
-
ensureRealDirectoryTree(ledgerHome, sessionDirectory);
|
|
1337
|
-
ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
|
|
1338
1471
|
|
|
1339
1472
|
const attachments = await freezeAttachments(options.attachmentPaths, attachmentsDirectory);
|
|
1340
1473
|
// Ticket binding is LLM-only post-admission (#635); admission stays unbound.
|
|
@@ -1418,9 +1551,11 @@ export async function loadAdmittedJudgeRequest(
|
|
|
1418
1551
|
}
|
|
1419
1552
|
|
|
1420
1553
|
export async function ensureRunArtifactsDir(runDirectory: string): Promise<string> {
|
|
1421
|
-
const
|
|
1422
|
-
|
|
1423
|
-
|
|
1554
|
+
const directory = roleRunArtifactsDirectory(runDirectory);
|
|
1555
|
+
return ensureRoleRunDirectory(
|
|
1556
|
+
resolveActivationLedgerHome(homeFromRunDirectory(runDirectory)),
|
|
1557
|
+
directory,
|
|
1558
|
+
);
|
|
1424
1559
|
}
|
|
1425
1560
|
|
|
1426
1561
|
export type AdmitCoderInvocationOptions = {
|
|
@@ -1464,17 +1599,16 @@ export async function admitCoderInvocation(
|
|
|
1464
1599
|
sessionDirectory,
|
|
1465
1600
|
sessionFile,
|
|
1466
1601
|
runDirectory,
|
|
1602
|
+
attachmentsDirectory,
|
|
1467
1603
|
ledgerHome,
|
|
1468
1604
|
bookKey,
|
|
1469
1605
|
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
1470
1606
|
cwd: projectRoot,
|
|
1471
1607
|
runId,
|
|
1472
1608
|
role: "coder",
|
|
1609
|
+
subject: { unbound: true },
|
|
1473
1610
|
home: options.home,
|
|
1474
1611
|
});
|
|
1475
|
-
const attachmentsDirectory = join(runDirectory, "attachments");
|
|
1476
|
-
ensureRealDirectoryTree(ledgerHome, sessionDirectory);
|
|
1477
|
-
ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
|
|
1478
1612
|
|
|
1479
1613
|
const attachments = await freezeAttachments(options.attachmentPaths, attachmentsDirectory);
|
|
1480
1614
|
|
|
@@ -1611,17 +1745,16 @@ export async function admitFixerInvocation(
|
|
|
1611
1745
|
sessionDirectory,
|
|
1612
1746
|
sessionFile,
|
|
1613
1747
|
runDirectory,
|
|
1748
|
+
attachmentsDirectory,
|
|
1614
1749
|
ledgerHome,
|
|
1615
1750
|
bookKey,
|
|
1616
1751
|
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
1617
1752
|
cwd: projectRoot,
|
|
1618
1753
|
runId,
|
|
1619
1754
|
role: "fixer",
|
|
1755
|
+
subject: { unbound: true },
|
|
1620
1756
|
home: options.home,
|
|
1621
1757
|
});
|
|
1622
|
-
const attachmentsDirectory = join(runDirectory, "attachments");
|
|
1623
|
-
ensureRealDirectoryTree(ledgerHome, sessionDirectory);
|
|
1624
|
-
ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
|
|
1625
1758
|
|
|
1626
1759
|
const attachments = await freezeAttachments(options.attachmentPaths, attachmentsDirectory);
|
|
1627
1760
|
|
|
@@ -1918,17 +2051,16 @@ export async function admitCollectorInvocation(
|
|
|
1918
2051
|
sessionDirectory,
|
|
1919
2052
|
sessionFile,
|
|
1920
2053
|
runDirectory,
|
|
2054
|
+
attachmentsDirectory,
|
|
1921
2055
|
ledgerHome,
|
|
1922
2056
|
bookKey,
|
|
1923
2057
|
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
1924
2058
|
cwd: projectRoot,
|
|
1925
2059
|
runId,
|
|
1926
2060
|
role: "collector",
|
|
2061
|
+
subject: { unbound: true },
|
|
1927
2062
|
home: options.home,
|
|
1928
2063
|
});
|
|
1929
|
-
const attachmentsDirectory = join(runDirectory, "attachments");
|
|
1930
|
-
ensureRealDirectoryTree(ledgerHome, sessionDirectory);
|
|
1931
|
-
ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
|
|
1932
2064
|
|
|
1933
2065
|
// #676 A: freeze task materials BEFORE target resolution so the role receives
|
|
1934
2066
|
// real instruction + attachments. Admission binds only explicit --pr or unique
|
|
@@ -2015,8 +2147,9 @@ export function buildCollectorTransportPrompt(
|
|
|
2015
2147
|
const DOCTOR_ISSUE_NUMBER_PATTERN = /^[1-9]\d*$/;
|
|
2016
2148
|
|
|
2017
2149
|
/** Match retained Doctor case runs roots (ADR 0017 / loadDoctorCase). */
|
|
2150
|
+
/** Canonical Doctor case: `<book>/<ticket>/runs`. Legacy `issues/<n>/runs` is read-only compat. */
|
|
2018
2151
|
const DOCTOR_CASE_RUNS_PATH_PATTERN =
|
|
2019
|
-
/\/\.ak-roles\/books\/[^/]+\/issues\/([1-9]\d*)\/runs$/;
|
|
2152
|
+
/\/\.ak-roles\/books\/[^/]+\/(?:issues\/)?([1-9]\d*)\/runs$/;
|
|
2020
2153
|
|
|
2021
2154
|
/**
|
|
2022
2155
|
* Parse a positive Issue number for public Doctor admission.
|
|
@@ -2131,9 +2264,9 @@ export async function resolveDoctorCaseRunsPath(options: {
|
|
|
2131
2264
|
runs?: string;
|
|
2132
2265
|
}): Promise<string> {
|
|
2133
2266
|
const ledgerHome = resolveActivationLedgerHome(options.home);
|
|
2267
|
+
// Canonical topology: <book>/<ticket>/runs (docs/dossier-topology.md).
|
|
2134
2268
|
const defaultRuns = join(
|
|
2135
2269
|
activationBookDirectory(ledgerHome, options.bookKey),
|
|
2136
|
-
"issues",
|
|
2137
2270
|
String(options.issueNumber),
|
|
2138
2271
|
"runs",
|
|
2139
2272
|
);
|
|
@@ -2177,7 +2310,7 @@ export async function resolveDoctorCaseRunsPath(options: {
|
|
|
2177
2310
|
const match = normalized.match(DOCTOR_CASE_RUNS_PATH_PATTERN);
|
|
2178
2311
|
if (!match) {
|
|
2179
2312
|
throw new CliUsageError(
|
|
2180
|
-
"doctor --runs must be an .ak-roles/books/<book
|
|
2313
|
+
"doctor --runs must be an .ak-roles/books/<book>/<n>/runs directory",
|
|
2181
2314
|
);
|
|
2182
2315
|
}
|
|
2183
2316
|
if (Number(match[1]) !== options.issueNumber) {
|
|
@@ -2217,12 +2350,14 @@ export async function admitDoctorInvocation(
|
|
|
2217
2350
|
sessionDirectory,
|
|
2218
2351
|
sessionFile,
|
|
2219
2352
|
runDirectory,
|
|
2353
|
+
attachmentsDirectory,
|
|
2220
2354
|
ledgerHome,
|
|
2221
2355
|
bookKey,
|
|
2222
2356
|
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
2223
2357
|
cwd: projectRoot,
|
|
2224
2358
|
runId,
|
|
2225
2359
|
role: "doctor",
|
|
2360
|
+
subject: { unbound: true },
|
|
2226
2361
|
home: options.home,
|
|
2227
2362
|
});
|
|
2228
2363
|
|
|
@@ -2266,9 +2401,6 @@ export async function admitDoctorInvocation(
|
|
|
2266
2401
|
);
|
|
2267
2402
|
}
|
|
2268
2403
|
|
|
2269
|
-
const attachmentsDirectory = join(runDirectory, "attachments");
|
|
2270
|
-
ensureRealDirectoryTree(ledgerHome, sessionDirectory);
|
|
2271
|
-
ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
|
|
2272
2404
|
|
|
2273
2405
|
const attachments = await freezeAttachments(options.attachmentPaths ?? [], attachmentsDirectory);
|
|
2274
2406
|
|
|
@@ -2423,24 +2555,27 @@ export async function admitNotaryInvocation(options: {
|
|
|
2423
2555
|
throw error;
|
|
2424
2556
|
}
|
|
2425
2557
|
|
|
2558
|
+
// Notary inherits board identity only — migration-derived stays display/placement.
|
|
2559
|
+
const inheritedTicketNumber = await readBoardTicketNumber(sourceRun.runDirectory);
|
|
2426
2560
|
const runId = options.createRunId?.() ?? uuidv7();
|
|
2427
2561
|
const {
|
|
2428
2562
|
principal,
|
|
2429
2563
|
sessionDirectory,
|
|
2430
2564
|
sessionFile,
|
|
2431
2565
|
runDirectory,
|
|
2566
|
+
attachmentsDirectory,
|
|
2432
2567
|
ledgerHome,
|
|
2433
2568
|
bookKey,
|
|
2434
2569
|
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
2435
2570
|
cwd: projectRoot,
|
|
2436
2571
|
runId,
|
|
2437
2572
|
role: "notary",
|
|
2573
|
+
subject: inheritedTicketNumber === undefined
|
|
2574
|
+
? { unbound: true }
|
|
2575
|
+
: { ticketNumber: inheritedTicketNumber },
|
|
2438
2576
|
home: options.home,
|
|
2439
2577
|
});
|
|
2440
|
-
|
|
2441
|
-
const ticketFields = ticketAdmissionFields(
|
|
2442
|
-
await readRunTicketNumber(sourceRun.runDirectory),
|
|
2443
|
-
);
|
|
2578
|
+
const ticketFields = ticketAdmissionFields(inheritedTicketNumber);
|
|
2444
2579
|
|
|
2445
2580
|
const admitted = {
|
|
2446
2581
|
role: "notary" as const,
|
|
@@ -2582,15 +2717,16 @@ export async function admitGleanerLeftInvocation(
|
|
|
2582
2717
|
sessionDirectory,
|
|
2583
2718
|
sessionFile,
|
|
2584
2719
|
runDirectory,
|
|
2720
|
+
attachmentsDirectory,
|
|
2585
2721
|
ledgerHome,
|
|
2586
2722
|
bookKey,
|
|
2587
2723
|
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
2588
2724
|
cwd: projectRoot,
|
|
2589
2725
|
runId,
|
|
2590
2726
|
role: "gleaner-left",
|
|
2727
|
+
subject: { unbound: true },
|
|
2591
2728
|
home: options.home,
|
|
2592
2729
|
});
|
|
2593
|
-
ensureRealDirectoryTree(ledgerHome, sessionDirectory);
|
|
2594
2730
|
|
|
2595
2731
|
const instruction = options.instruction;
|
|
2596
2732
|
const instructionEmpty = instruction.trim() === "";
|
|
@@ -2746,17 +2882,16 @@ export async function admitReviewerInvocation(
|
|
|
2746
2882
|
sessionDirectory,
|
|
2747
2883
|
sessionFile,
|
|
2748
2884
|
runDirectory,
|
|
2885
|
+
attachmentsDirectory,
|
|
2749
2886
|
ledgerHome,
|
|
2750
2887
|
bookKey,
|
|
2751
2888
|
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
2752
2889
|
cwd: projectRoot,
|
|
2753
2890
|
runId,
|
|
2754
2891
|
role: "reviewer",
|
|
2892
|
+
subject: { unbound: true },
|
|
2755
2893
|
home: options.home,
|
|
2756
2894
|
});
|
|
2757
|
-
const attachmentsDirectory = join(runDirectory, "attachments");
|
|
2758
|
-
ensureRealDirectoryTree(ledgerHome, sessionDirectory);
|
|
2759
|
-
ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
|
|
2760
2895
|
|
|
2761
2896
|
// Public parse already rejects attachments; keep freeze loop for structural symmetry.
|
|
2762
2897
|
const attachments = await freezeAttachments(options.attachmentPaths, attachmentsDirectory);
|
|
@@ -2955,17 +3090,16 @@ export async function admitMergerInvocation(
|
|
|
2955
3090
|
sessionDirectory,
|
|
2956
3091
|
sessionFile,
|
|
2957
3092
|
runDirectory,
|
|
3093
|
+
attachmentsDirectory,
|
|
2958
3094
|
ledgerHome,
|
|
2959
3095
|
bookKey,
|
|
2960
3096
|
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
2961
3097
|
cwd: projectRoot,
|
|
2962
3098
|
runId,
|
|
2963
3099
|
role: "merger",
|
|
3100
|
+
subject: { unbound: true },
|
|
2964
3101
|
home: options.home,
|
|
2965
3102
|
});
|
|
2966
|
-
const attachmentsDirectory = join(runDirectory, "attachments");
|
|
2967
|
-
ensureRealDirectoryTree(ledgerHome, sessionDirectory);
|
|
2968
|
-
ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
|
|
2969
3103
|
|
|
2970
3104
|
const attachments = await freezeAttachments(options.attachmentPaths, attachmentsDirectory);
|
|
2971
3105
|
|
|
@@ -645,8 +645,8 @@ const DOCTOR_OPTIONS = [
|
|
|
645
645
|
repeatable: false,
|
|
646
646
|
form: "option",
|
|
647
647
|
description: {
|
|
648
|
-
en: "Optional project-relative .ak-roles/books/<book
|
|
649
|
-
zh: "可选项目相对 .ak-roles/books/<book
|
|
648
|
+
en: "Optional project-relative .ak-roles/books/<book>/<n>/runs override matching --issue.",
|
|
649
|
+
zh: "可选项目相对 .ak-roles/books/<book>/<n>/runs 覆盖,且须匹配 --issue。",
|
|
650
650
|
},
|
|
651
651
|
},
|
|
652
652
|
] as const satisfies readonly PublicOptionDefinition[];
|