@akagilnc/pi-workflow-roles 0.1.5478 → 0.1.5504
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 +4945 -4980
- package/dist/analyst-gate-cycles-read.js +11 -29
- package/dist/analyst-ledger.js +10 -12
- package/dist/archivist-record-entry.js +4 -3
- package/dist/archivist-record-pointer.js +59 -32
- package/dist/atomic-write.js +2 -2
- package/dist/auditor-dossier-tool.js +8 -4
- package/dist/auditor-soul.js +2 -1
- package/dist/collector-config.js +6 -5
- package/dist/doctor-evidence.js +5 -1
- package/dist/engine-detour-usage.js +4 -10
- package/dist/headless-host/description.js +251 -0
- package/dist/headless-host/production-host.js +2787 -2658
- package/dist/ledger-session-read.js +5 -0
- package/dist/migrate-book-topology.js +129 -100
- package/dist/navigator-public-session.js +13 -17
- package/dist/notary-source-run.js +7 -15
- package/dist/packaged-role-registry.js +0 -12
- package/dist/pi/durable-principal.js +2 -3
- package/dist/pi/role-turn-host.js +27 -13
- package/dist/public-cli/instruction-seat-run.js +4 -4
- package/dist/public-cli/invocation.js +17 -45
- package/dist/public-cli/main.js +1074 -1085
- package/dist/public-cli/run-lifecycle.js +17 -47
- package/dist/public-cli/settlement.js +28 -128
- package/dist/public-cli/terminal.js +0 -12
- package/dist/role-run-placement.js +52 -5
- package/dist/role-run-relocation.js +39 -2
- package/dist/run-terminal-artifacts.js +2 -6
- package/dist/run-ticket-number.js +33 -4
- package/dist/session-assistant-usage.js +9 -18
- package/dist/session-identity.js +3 -4
- package/dist/sitian-appender.js +20 -6
- package/dist/submission-ledger.js +4 -4
- package/dist/ticket-provenance-contracts.js +2 -8
- package/dist/ticket-provenance.js +5 -4
- package/package.json +1 -1
- package/src/acp-host/role-turn-host.ts +2 -2
- package/src/analyst-gate-cycles-read.ts +11 -34
- package/src/analyst-ledger.ts +14 -14
- package/src/archivist-record-entry.ts +4 -3
- package/src/archivist-record-pointer.ts +70 -34
- package/src/atomic-write.ts +3 -1
- package/src/auditor-dossier-tool.ts +8 -4
- package/src/auditor-soul.ts +2 -1
- package/src/collector-config.ts +5 -3
- package/src/doctor-evidence.ts +5 -1
- package/src/engine-detour-usage.ts +10 -13
- package/src/factory-board.ts +16 -81
- package/src/headless-host/description.ts +248 -1
- package/src/headless-host/production-host.ts +1 -2
- package/src/headless-host/role-turn-host.ts +10 -9
- package/src/ledger-session-read.ts +6 -0
- package/src/navigator-public-session.ts +13 -18
- package/src/navigator-work-context.ts +3 -1
- package/src/notary-role.ts +2 -1
- package/src/notary-source-run.ts +7 -17
- package/src/packaged-role-registry.ts +0 -15
- package/src/pi/durable-principal.ts +2 -4
- package/src/pi/role-turn-host.ts +35 -12
- package/src/public-cli/instruction-seat-run.ts +4 -4
- package/src/public-cli/invocation.ts +29 -57
- package/src/public-cli/run-lifecycle.ts +20 -43
- package/src/public-cli/settlement.ts +30 -173
- package/src/public-cli/terminal.ts +0 -60
- package/src/role-envelope.ts +4 -2
- package/src/role-run-placement.ts +63 -4
- package/src/role-run-relocation.ts +39 -2
- package/src/role-runtime.ts +40 -59
- package/src/run-terminal-artifacts.ts +3 -5
- package/src/run-ticket-number.ts +41 -3
- package/src/session-assistant-usage.ts +8 -20
- package/src/session-identity.ts +3 -4
- package/src/sitian-appender.ts +20 -12
- package/src/submission-ledger.ts +4 -4
- package/src/ticket-provenance-contracts.ts +3 -6
- package/src/ticket-provenance.ts +5 -6
- package/src/ticket-snapshot.ts +3 -2
- package/src/ticket-trajectory.ts +85 -99
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { sitianReportSafe } from "./host-session-record.js";
|
|
4
4
|
import {
|
|
5
5
|
NavigatorUnavailableError,
|
|
6
6
|
navigatorProviderFailureFromPublicTerminal,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
parseNavigatorModelSetting
|
|
10
10
|
} from "./navigator-session-contracts.js";
|
|
11
11
|
import { runDirectoryFromHostContext } from "./host-contracts.js";
|
|
12
|
+
import { parseRunLeaf } from "./role-run-placement.js";
|
|
12
13
|
import { CliUsageError } from "./public-cli/cli-errors.js";
|
|
13
14
|
import { isNavigatorSeat } from "./packaged-role-registry.js";
|
|
14
15
|
import {
|
|
@@ -32,11 +33,9 @@ async function resolveNavigatorLedgerHome(context) {
|
|
|
32
33
|
}
|
|
33
34
|
const NAVIGATOR_HOST_RUN_POINTER_ENTRY = "ak-navigator-host-run";
|
|
34
35
|
function runIdFromNavigatorDirectory(runDirectory) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const runId = entry.slice(0, entry.length - suffix.length);
|
|
39
|
-
return runId.length > 0 ? runId : void 0;
|
|
36
|
+
const parsed = parseRunLeaf(basename(runDirectory));
|
|
37
|
+
if (parsed === void 0 || parsed.role !== "navigator") return void 0;
|
|
38
|
+
return parsed.runId;
|
|
40
39
|
}
|
|
41
40
|
function readNavigatorHostRunPointer(entries) {
|
|
42
41
|
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
@@ -188,17 +187,14 @@ function createNativeNavigatorSessionFactory(deps) {
|
|
|
188
187
|
routePlaybookReadFailure: () => routePlaybookReadFailure,
|
|
189
188
|
appendEntry: (customType, data) => {
|
|
190
189
|
sessionManager.appendCustomEntry(customType, data);
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
});
|
|
200
|
-
} catch {
|
|
201
|
-
}
|
|
190
|
+
sitianReportSafe({
|
|
191
|
+
level: "event",
|
|
192
|
+
kind: "attendance",
|
|
193
|
+
cwd: context.cwd,
|
|
194
|
+
sessionParent: sessionManager.getSessionFile(),
|
|
195
|
+
payload: { customType, data },
|
|
196
|
+
source: "navigator-public-session"
|
|
197
|
+
});
|
|
202
198
|
},
|
|
203
199
|
entries: () => sessionManager.getEntries(),
|
|
204
200
|
setModel: async (next, nextThinking) => {
|
|
@@ -8,6 +8,7 @@ import { lstat, realpath } from "node:fs/promises";
|
|
|
8
8
|
import { resolveBookKeyFromGit } from "./activation-ledger-git.js";
|
|
9
9
|
import { activationBookDirectory, physicalPathIdentity, resolveActivationLedgerHome, } from "./activation-ledger-topology.js";
|
|
10
10
|
import { findRunDirectoryById, readRoleRunIdentity } from "./public-cli/run-lifecycle.js";
|
|
11
|
+
import { formatRunLeaf, parseRunLeaf } from "./role-run-placement.js";
|
|
11
12
|
async function readRetainedIdentity(runDirectory) {
|
|
12
13
|
try {
|
|
13
14
|
return await readRoleRunIdentity(runDirectory);
|
|
@@ -20,21 +21,12 @@ async function readRetainedIdentity(runDirectory) {
|
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
|
-
// Run directory leaf: <runId>@<role>. Production uses uuidv7 runIds; offline tracers
|
|
24
|
-
// may use deterministic non-uuid leaves. Role token stays identifier-shaped.
|
|
25
|
-
const RUN_DIR_NAME = /^([A-Za-z0-9][A-Za-z0-9._-]*)@([A-Za-z][A-Za-z0-9_-]*)$/;
|
|
26
24
|
export class NotarySourceRunError extends Error {
|
|
27
25
|
constructor(message, options) {
|
|
28
26
|
super(message, options);
|
|
29
27
|
this.name = "NotarySourceRunError";
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
|
-
function parseRunDirectoryName(name) {
|
|
33
|
-
const match = RUN_DIR_NAME.exec(name);
|
|
34
|
-
if (match === null)
|
|
35
|
-
return undefined;
|
|
36
|
-
return { runId: match[1], role: match[2] };
|
|
37
|
-
}
|
|
38
30
|
async function requireRunDirectory(candidate, display) {
|
|
39
31
|
let real;
|
|
40
32
|
try {
|
|
@@ -53,7 +45,7 @@ async function requireRunDirectory(candidate, display) {
|
|
|
53
45
|
if (!stat.isDirectory()) {
|
|
54
46
|
throw new NotarySourceRunError(`notary --source-run must be a run directory: ${display}`);
|
|
55
47
|
}
|
|
56
|
-
const identity =
|
|
48
|
+
const identity = parseRunLeaf(basename(real));
|
|
57
49
|
if (identity === undefined) {
|
|
58
50
|
throw new NotarySourceRunError(`notary --source-run must be named <runId>@<role>: ${basename(real)}`);
|
|
59
51
|
}
|
|
@@ -78,14 +70,14 @@ export async function resolveNotarySourceRunLocator(options) {
|
|
|
78
70
|
const bookDirectory = activationBookDirectory(ledgerHome, bookKey);
|
|
79
71
|
const bookRunsRoot = join(bookDirectory, "runs");
|
|
80
72
|
let candidate;
|
|
81
|
-
const bare =
|
|
73
|
+
const bare = parseRunLeaf(raw);
|
|
82
74
|
if (bare !== undefined && !raw.includes("/") && !raw.includes("\\")) {
|
|
83
75
|
candidate = (await findRunDirectoryById(options.home, bare.runId, bookKey, bare.role))
|
|
84
|
-
?? join(bookRunsRoot,
|
|
76
|
+
?? join(bookRunsRoot, formatRunLeaf(bare.runId, bare.role));
|
|
85
77
|
}
|
|
86
78
|
else {
|
|
87
79
|
candidate = isAbsolute(raw) ? raw : resolve(options.projectRoot, raw);
|
|
88
|
-
const identity =
|
|
80
|
+
const identity = parseRunLeaf(basename(candidate));
|
|
89
81
|
const subjectDirectory = dirname(dirname(candidate));
|
|
90
82
|
const isLegacyUnboundLocator = identity !== undefined &&
|
|
91
83
|
basename(dirname(candidate)) === "runs" &&
|
|
@@ -96,7 +88,7 @@ export async function resolveNotarySourceRunLocator(options) {
|
|
|
96
88
|
}
|
|
97
89
|
}
|
|
98
90
|
const real = await requireRunDirectory(candidate, raw);
|
|
99
|
-
const identity =
|
|
91
|
+
const identity = parseRunLeaf(basename(real));
|
|
100
92
|
const bookIdentity = physicalPathIdentity(bookDirectory);
|
|
101
93
|
const parentIdentity = physicalPathIdentity(dirname(real));
|
|
102
94
|
const subjectBookIdentity = physicalPathIdentity(dirname(dirname(dirname(real))));
|
|
@@ -128,7 +120,7 @@ export async function resolveNotarySourceRunLocator(options) {
|
|
|
128
120
|
/** Internal activation loader: path is the admitted absolute source run directory. */
|
|
129
121
|
export async function loadNotarySourceRunLocator(path) {
|
|
130
122
|
const real = await requireRunDirectory(path, path);
|
|
131
|
-
const identity =
|
|
123
|
+
const identity = parseRunLeaf(basename(real));
|
|
132
124
|
const runState = await readRetainedIdentity(real);
|
|
133
125
|
if (runState === undefined) {
|
|
134
126
|
throw new NotarySourceRunError("notary source-run lacks retained run-state identity");
|
|
@@ -297,8 +297,6 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
297
297
|
sourceRunStored: "raw",
|
|
298
298
|
argvResult: "source-run",
|
|
299
299
|
sameParent: "source-locator",
|
|
300
|
-
/** Infrastructure failure at this stage has no accepted gate cycle to project. */
|
|
301
|
-
skipGateOnInfrastructureStage: true,
|
|
302
300
|
/** 符宝郎 is a review officer and inherits the gatekeeper model. */
|
|
303
301
|
reviewOfficer: true,
|
|
304
302
|
provinceConfig: true,
|
|
@@ -406,8 +404,6 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
406
404
|
sameParent: "gate-pointer",
|
|
407
405
|
/** Resume restores an optional source-run path onto the admitted face. */
|
|
408
406
|
resumeSourcePath: true,
|
|
409
|
-
/** Infrastructure failure at this stage has no accepted gate cycle to project. */
|
|
410
|
-
skipGateOnInfrastructureStage: true,
|
|
411
407
|
/** 台院 is a review officer and inherits the gatekeeper model. */
|
|
412
408
|
reviewOfficer: true,
|
|
413
409
|
provinceConfig: true,
|
|
@@ -439,8 +435,6 @@ const PUBLIC_ROLE_RECORDS = [
|
|
|
439
435
|
presentSettled: "always",
|
|
440
436
|
summonResume: true,
|
|
441
437
|
admission: "instruction",
|
|
442
|
-
/** Infrastructure failure at this stage has no accepted gate cycle to project. */
|
|
443
|
-
skipGateOnInfrastructureStage: true,
|
|
444
438
|
sameParent: "none",
|
|
445
439
|
/** Province model root. Officers name this seat via modelInheritsFrom. */
|
|
446
440
|
provinceConfig: true,
|
|
@@ -608,11 +602,6 @@ function packagedAuditToolName(role) {
|
|
|
608
602
|
if (record === void 0 || !("auditTool" in record)) return void 0;
|
|
609
603
|
return record.auditTool;
|
|
610
604
|
}
|
|
611
|
-
function packagedSkipsGateOnInfrastructureStage(stage) {
|
|
612
|
-
return typeof stage === "string" && PUBLIC_ROLE_RECORDS.some(
|
|
613
|
-
(record) => "skipGateOnInfrastructureStage" in record && record.skipGateOnInfrastructureStage === true && record.role === stage
|
|
614
|
-
);
|
|
615
|
-
}
|
|
616
605
|
function packagedDurableOfficerEntry(officer) {
|
|
617
606
|
return typeof officer === "string" && PUBLIC_ROLE_RECORDS.some(
|
|
618
607
|
(record) => "durableOfficerEntry" in record && record.durableOfficerEntry === true && record.role === officer
|
|
@@ -699,7 +688,6 @@ export {
|
|
|
699
688
|
packagedRoleOutputTool,
|
|
700
689
|
packagedRolePhaseFlag,
|
|
701
690
|
packagedRoleSessionMaterials,
|
|
702
|
-
packagedSkipsGateOnInfrastructureStage,
|
|
703
691
|
packagedStoresSourceRunRaw,
|
|
704
692
|
packagedSubjectChoices
|
|
705
693
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
1
|
import { resolveBookKeyFromGit } from "../activation-ledger-git.js";
|
|
3
2
|
import { resolveActivationLedgerHome } from "../activation-ledger-topology.js";
|
|
4
|
-
import { roleRunPlacement } from "../role-run-placement.js";
|
|
3
|
+
import { roleRunPlacement, sessionFileIn } from "../role-run-placement.js";
|
|
5
4
|
function encode(coordinates) {
|
|
6
5
|
return coordinates;
|
|
7
6
|
}
|
|
@@ -47,7 +46,7 @@ export const piDurablePrincipalAuthority = {
|
|
|
47
46
|
sessionDirectory: record.sessionDirectory,
|
|
48
47
|
sessionFile: typeof record.sessionFile === "string" && record.sessionFile.trim() !== ""
|
|
49
48
|
? record.sessionFile
|
|
50
|
-
:
|
|
49
|
+
: sessionFileIn(record.sessionDirectory),
|
|
51
50
|
};
|
|
52
51
|
},
|
|
53
52
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { execFile, spawn } from "node:child_process";
|
|
7
7
|
import { constants } from "node:fs";
|
|
8
|
-
import { access, appendFile,
|
|
8
|
+
import { access, appendFile, realpath } from "node:fs/promises";
|
|
9
9
|
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
10
10
|
import { platform } from "node:process";
|
|
11
11
|
import { promisify } from "node:util";
|
|
@@ -14,6 +14,7 @@ import { ExplicitInternalActivationError } from "../host-contracts.js";
|
|
|
14
14
|
import { applyEngineChildEnv, ENGINE_MODEL_FLAG_NAME, normalizeEngineName } from "../engine-detour.js";
|
|
15
15
|
import { projectActivationFlags } from "../role-activation-flags.js";
|
|
16
16
|
import { encodeUserDialogueStdin } from "../user-dialogue-stdin.js";
|
|
17
|
+
import { readStrictPiSessionJsonl } from "../ledger-session-read.js";
|
|
17
18
|
import { projectThrownFailureLeaf, retainPackageFault } from "../public-cli/settlement.js";
|
|
18
19
|
import { describeErrorIdentity } from "../public-cli/run-lifecycle.js";
|
|
19
20
|
import { RECEIPT_DELIVERY_LIMIT_ENV } from "../receipt-delivery-policy.js";
|
|
@@ -361,28 +362,41 @@ export function createPiRoleTurnHost(config) {
|
|
|
361
362
|
};
|
|
362
363
|
}
|
|
363
364
|
import { sitianReport } from "../sitian-facade.js";
|
|
365
|
+
/** Last non-session entry id. Session headers are not parents of custom lines. */
|
|
366
|
+
export function piSessionCustomParentId(entries) {
|
|
367
|
+
let parentId = null;
|
|
368
|
+
for (const entry of entries) {
|
|
369
|
+
if (typeof entry.id === "string" && entry.type !== "session")
|
|
370
|
+
parentId = entry.id;
|
|
371
|
+
}
|
|
372
|
+
return parentId;
|
|
373
|
+
}
|
|
374
|
+
/** One Pi custom JSONL line: type, customType, data, id, parentId, timestamp. */
|
|
375
|
+
export function formatPiSessionCustomEntry(input) {
|
|
376
|
+
return `${JSON.stringify({
|
|
377
|
+
type: "custom",
|
|
378
|
+
customType: input.customType,
|
|
379
|
+
data: input.data,
|
|
380
|
+
id: randomUUID(),
|
|
381
|
+
parentId: input.parentId,
|
|
382
|
+
timestamp: input.timestamp,
|
|
383
|
+
})}\n`;
|
|
384
|
+
}
|
|
364
385
|
/**
|
|
365
386
|
* Append one custom JSONL entry to the durable principal's session file.
|
|
366
387
|
* Pi session codec only — AK artifact O_EXCL retention stays in public-cli.
|
|
388
|
+
* Read is raw JSON.parse (SyntaxError propagates). Sitian full-traversal stays elsewhere.
|
|
367
389
|
*/
|
|
368
390
|
export async function appendPiSessionCustomEntry(authority, principal, customType, data) {
|
|
369
391
|
const { sessionFile } = authority.decode(principal);
|
|
370
|
-
const
|
|
371
|
-
let parentId = null;
|
|
372
|
-
for (const line of text.trim().split("\n").filter(Boolean)) {
|
|
373
|
-
const entry = JSON.parse(line);
|
|
374
|
-
if (typeof entry.id === "string" && entry.type !== "session")
|
|
375
|
-
parentId = entry.id;
|
|
376
|
-
}
|
|
392
|
+
const entries = await readStrictPiSessionJsonl(sessionFile);
|
|
377
393
|
const timestamp = new Date().toISOString();
|
|
378
|
-
const pointerLine =
|
|
379
|
-
type: "custom",
|
|
394
|
+
const pointerLine = formatPiSessionCustomEntry({
|
|
380
395
|
customType,
|
|
381
396
|
data,
|
|
382
|
-
|
|
383
|
-
parentId,
|
|
397
|
+
parentId: piSessionCustomParentId(entries),
|
|
384
398
|
timestamp,
|
|
385
|
-
})
|
|
399
|
+
});
|
|
386
400
|
await appendFile(sessionFile, pointerLine, "utf8");
|
|
387
401
|
sitianReport({
|
|
388
402
|
level: "event",
|
|
@@ -33,7 +33,7 @@ import { presentControlledFailure, packageFaultNoteFor, prepareSummonsResumeMate
|
|
|
33
33
|
import { presentTerminal } from "./auto-resume.js";
|
|
34
34
|
import { buildAutoResumeContinuationPrompt, loadResumablePublicRole, markRunAdmitted, parentRunPathFromGatePointerInstruction, readCurrentCourt, } from "./run-lifecycle.js";
|
|
35
35
|
import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.js";
|
|
36
|
-
import { presentStructuralRejection, readBoundSessionEntries,
|
|
36
|
+
import { presentStructuralRejection, readBoundSessionEntries, attachPostAuditCountersignFact, trySettlePublicSeat, } from "./settlement.js";
|
|
37
37
|
import { warnMissingMethodSkills } from "./machine-method-skills.js";
|
|
38
38
|
import { formatTerminalResult, isLawfulTypedTerminalOutcome, } from "./terminal.js";
|
|
39
39
|
import { admittedSeatTurnDetails, projectRoleTurnRequest, requiredMethodSkills, } from "./turn-request.js";
|
|
@@ -1151,11 +1151,11 @@ async function auditSubmittedRole(turn, env, io, resumedOfficer, passedReceipt)
|
|
|
1151
1151
|
}
|
|
1152
1152
|
}
|
|
1153
1153
|
// The turn already settled this court (payloads, usage, autoResumeCount, history).
|
|
1154
|
-
// Audit only adds
|
|
1155
|
-
//
|
|
1154
|
+
// Audit only adds, for 中书省, the officer fact. A second settle would
|
|
1155
|
+
// republish the same attempt.
|
|
1156
1156
|
if (turn.terminal === undefined)
|
|
1157
1157
|
throw new Error(`audited ${admitted.role} submission did not settle`);
|
|
1158
|
-
const terminal = await
|
|
1158
|
+
const terminal = await attachPostAuditCountersignFact(admitted, env.principalAuthority, turn.terminal, packageFaultScope(admitted, env, io));
|
|
1159
1159
|
turn = { ...turn, terminal };
|
|
1160
1160
|
await presentTerminal(terminal, { ...io, omitFailureStderrDiagnostic: true }, admitted.runDirectory);
|
|
1161
1161
|
return turn;
|
|
@@ -9,9 +9,9 @@ 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, isUnboundRunDirectory, listBookRunDirectories, roleRunArtifactsDirectory, roleRunPlacement, } from "../role-run-placement.js";
|
|
12
|
+
import { ensureRoleRunDirectory, ensureRoleRunPlacement, formatRunLeaf, 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
|
-
import { isSafePositiveTicketNumber, readBoardTicketNumber, requireSafePositiveTicketNumber, } from "../run-ticket-number.js";
|
|
14
|
+
import { isSafePositiveTicketNumber, parseTicketNumber, readBoardTicketNumber, requireSafePositiveTicketNumber, } from "../run-ticket-number.js";
|
|
15
15
|
import { rewriteRunDirectoryPathFields, rewriteRunDirectoryPathValue, } from "../role-run-relocation.js";
|
|
16
16
|
import { loadDoctorCase, } from "../doctor-evidence.js";
|
|
17
17
|
import { emptyCollectorManifest, loadCollectorManifest, parseCollectorPrNumber, parseCollectorRepository, } from "../collector-config.js";
|
|
@@ -230,7 +230,7 @@ export async function relocateAdmittedRunToTicket(admitted, authority, heldLease
|
|
|
230
230
|
for (const childRunId of Array.isArray(childRunIds) ? childRunIds : []) {
|
|
231
231
|
if (typeof childRunId !== "string")
|
|
232
232
|
continue;
|
|
233
|
-
const childDirectory = join(dirname(oldRunDirectory),
|
|
233
|
+
const childDirectory = join(dirname(oldRunDirectory), formatRunLeaf(childRunId, "diarist"));
|
|
234
234
|
// A child that already filed under a ticket keeps its own assertion.
|
|
235
235
|
if (!existsSync(childDirectory))
|
|
236
236
|
continue;
|
|
@@ -334,17 +334,16 @@ export async function recordLaunchedRolePackageIdentity(runDirectory, identity)
|
|
|
334
334
|
entryMode: identity.entryMode,
|
|
335
335
|
});
|
|
336
336
|
}
|
|
337
|
+
/** Trim, then the shared ticket spelling. Callers keep their own diagnostic text. */
|
|
338
|
+
function rejectUnlessTicketNumber(raw, message) {
|
|
339
|
+
const parsed = parseTicketNumber(raw.trim());
|
|
340
|
+
if (parsed === undefined)
|
|
341
|
+
throw new CliUsageError(message);
|
|
342
|
+
return parsed;
|
|
343
|
+
}
|
|
337
344
|
/** Positive ticket number for analyst query-scope face (and shared integer parse). */
|
|
338
345
|
export function parsePositiveTicketNumber(raw, flag) {
|
|
339
|
-
|
|
340
|
-
if (!ANALYST_TICKET_NUMBER_PATTERN.test(trimmed)) {
|
|
341
|
-
throw new CliUsageError(`${flag} must be a positive integer, got ${raw}`);
|
|
342
|
-
}
|
|
343
|
-
const value = Number(trimmed);
|
|
344
|
-
if (!Number.isSafeInteger(value) || value < 1) {
|
|
345
|
-
throw new CliUsageError(`${flag} must be a positive integer, got ${raw}`);
|
|
346
|
-
}
|
|
347
|
-
return value;
|
|
346
|
+
return rejectUnlessTicketNumber(raw, `${flag} must be a positive integer, got ${raw}`);
|
|
348
347
|
}
|
|
349
348
|
/**
|
|
350
349
|
* One token scan for public argv.
|
|
@@ -957,7 +956,7 @@ export async function admitPublicRole(role, parsed, env, override) {
|
|
|
957
956
|
}
|
|
958
957
|
let caseIdentity;
|
|
959
958
|
try {
|
|
960
|
-
const patient = await loadDoctorCase(caseRunsPath);
|
|
959
|
+
const patient = await loadDoctorCase(caseRunsPath, parsed.runs === undefined ? undefined : placed.projectRoot);
|
|
961
960
|
if (patient.identity.issueNumber !== issueNumber) {
|
|
962
961
|
throw new CliUsageError(`doctor case issue ${patient.identity.issueNumber} does not match --issue ${issueNumber}`);
|
|
963
962
|
}
|
|
@@ -1411,26 +1410,17 @@ function isGitRemoteMissing(error) {
|
|
|
1411
1410
|
const status = error.status;
|
|
1412
1411
|
return status === 2;
|
|
1413
1412
|
}
|
|
1414
|
-
/** Positive Issue number grammar shared with Doctor case path identity. */
|
|
1415
|
-
const DOCTOR_ISSUE_NUMBER_PATTERN = /^[1-9]\d*$/;
|
|
1416
|
-
/** Match retained Doctor case runs roots (ADR 0017 / loadDoctorCase). */
|
|
1417
|
-
/** Canonical Doctor case: `<book>/<ticket>/runs`. Legacy `issues/<n>/runs` is read-only compat. */
|
|
1418
|
-
const DOCTOR_CASE_RUNS_PATH_PATTERN = /\/\.ak-roles\/books\/[^/]+\/(?:issues\/)?([1-9]\d*)\/runs$/;
|
|
1419
1413
|
/**
|
|
1420
1414
|
* Parse a positive Issue number for public Doctor admission.
|
|
1421
1415
|
* Leading zeros and non-integers are structural rejects.
|
|
1422
1416
|
*/
|
|
1423
1417
|
export function parseDoctorIssueNumber(raw) {
|
|
1424
|
-
|
|
1425
|
-
if (!DOCTOR_ISSUE_NUMBER_PATTERN.test(trimmed)) {
|
|
1426
|
-
throw new CliUsageError(`doctor --issue must be a positive integer, got ${raw}`);
|
|
1427
|
-
}
|
|
1428
|
-
return Number(trimmed);
|
|
1418
|
+
return rejectUnlessTicketNumber(raw, `doctor --issue must be a positive integer, got ${raw}`);
|
|
1429
1419
|
}
|
|
1430
1420
|
/**
|
|
1431
1421
|
* Resolve the retained Doctor case runs root from Issue identity.
|
|
1432
|
-
* Default is the #78 book locator; optional --runs must stay project-confined
|
|
1433
|
-
*
|
|
1422
|
+
* Default is the #78 book locator; optional --runs must stay project-confined.
|
|
1423
|
+
* loadDoctorCase owns retained-root grammar at IO; admission matches its case identity.
|
|
1434
1424
|
*/
|
|
1435
1425
|
export async function resolveDoctorCaseRunsPath(options) {
|
|
1436
1426
|
const ledgerHome = resolveActivationLedgerHome(options.home);
|
|
@@ -1460,14 +1450,6 @@ export async function resolveDoctorCaseRunsPath(options) {
|
|
|
1460
1450
|
const detail = errorText(error);
|
|
1461
1451
|
throw new CliUsageError(`doctor --runs is not a readable retained runs root: ${detail}`, { cause: error });
|
|
1462
1452
|
}
|
|
1463
|
-
const normalized = real.split(sep).join("/");
|
|
1464
|
-
const match = normalized.match(DOCTOR_CASE_RUNS_PATH_PATTERN);
|
|
1465
|
-
if (!match) {
|
|
1466
|
-
throw new CliUsageError("doctor --runs must be an .ak-roles/books/<book>/<n>/runs directory");
|
|
1467
|
-
}
|
|
1468
|
-
if (Number(match[1]) !== options.issueNumber) {
|
|
1469
|
-
throw new CliUsageError(`doctor --runs issue ${match[1]} does not match --issue ${options.issueNumber}`);
|
|
1470
|
-
}
|
|
1471
1453
|
return real;
|
|
1472
1454
|
}
|
|
1473
1455
|
/** Frozen Skill arg projection shared by initial and resume (never reverse-parsed). */
|
|
@@ -1501,23 +1483,13 @@ export async function deriveMergerEnvelopeFromActiveMerge(projectRoot, gitState
|
|
|
1501
1483
|
resolutionScope,
|
|
1502
1484
|
});
|
|
1503
1485
|
}
|
|
1504
|
-
const ANALYST_TICKET_NUMBER_PATTERN = /^[1-9]\d*$/;
|
|
1505
1486
|
/**
|
|
1506
1487
|
* Parse a positive ticket / issue number for public analyst admission.
|
|
1507
1488
|
* Leading zeros and non-integers are structural rejects (same face as #176).
|
|
1508
1489
|
* `flag` names the actual argv face in diagnostics (cohort group lists reuse this).
|
|
1509
1490
|
*/
|
|
1510
1491
|
export function parseAnalystTicketNumber(raw, flag = "--ticket") {
|
|
1511
|
-
|
|
1512
|
-
if (!ANALYST_TICKET_NUMBER_PATTERN.test(trimmed)) {
|
|
1513
|
-
throw new CliUsageError(`analyst ${flag} must be a positive integer, got ${raw}`);
|
|
1514
|
-
}
|
|
1515
|
-
const value = Number(trimmed);
|
|
1516
|
-
// Digit-only strings beyond MAX_SAFE_INTEGER round or become Infinity — reject.
|
|
1517
|
-
if (!Number.isSafeInteger(value) || value < 1) {
|
|
1518
|
-
throw new CliUsageError(`analyst ${flag} must be a positive integer, got ${raw}`);
|
|
1519
|
-
}
|
|
1520
|
-
return value;
|
|
1492
|
+
return rejectUnlessTicketNumber(raw, `analyst ${flag} must be a positive integer, got ${raw}`);
|
|
1521
1493
|
}
|
|
1522
1494
|
/**
|
|
1523
1495
|
* Parse one cohort issue token: bare positive integer or `book:N`.
|
|
@@ -1532,7 +1504,7 @@ export function parseAnalystCohortIssueToken(raw, flag) {
|
|
|
1532
1504
|
const sep = trimmed.lastIndexOf(":");
|
|
1533
1505
|
if (sep > 0) {
|
|
1534
1506
|
const rhs = trimmed.slice(sep + 1);
|
|
1535
|
-
if (
|
|
1507
|
+
if (parseTicketNumber(rhs) !== undefined) {
|
|
1536
1508
|
const bookKey = trimmed.slice(0, sep);
|
|
1537
1509
|
if (bookKey.trim() === "") {
|
|
1538
1510
|
throw new CliUsageError(`${flag} book:N requires a non-empty book key, got ${raw}`);
|