@akagilnc/pi-workflow-roles 0.1.5614 → 0.1.5650
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 +960 -465
- package/dist/activation-ledger-topology.js +12 -0
- package/dist/external-host-turn-loop.js +281 -0
- package/dist/headless-host/production-host.js +900 -406
- package/dist/migrate-book-topology.js +8 -5
- package/dist/pi/role-turn-host.js +33 -3
- package/dist/public-cli/instruction-seat-run.js +177 -46
- package/dist/public-cli/invocation.js +5 -12
- package/dist/public-cli/main.js +541 -165
- package/dist/public-cli/post-admission.js +171 -18
- package/dist/public-cli/settlement.js +20 -10
- package/dist/report-ticket-tool.js +136 -0
- package/dist/role-run-placement.js +1 -2
- package/dist/role-run-relocation.js +24 -4
- package/package.json +1 -1
- package/resources/engine-dispatch.md +6 -3
- package/resources/engines/claude-code.md +4 -5
- package/resources/missing-ticket-reask.md +1 -0
- package/src/acp-host/role-turn-host.ts +41 -31
- package/src/activation-ledger-topology.ts +16 -0
- package/src/external-host-turn-loop.ts +103 -12
- package/src/headless-host/role-turn-host.ts +41 -25
- package/src/pi/role-turn-host.ts +37 -3
- package/src/public-cli/instruction-seat-run.ts +248 -50
- package/src/public-cli/invocation.ts +15 -27
- package/src/public-cli/post-admission.ts +250 -33
- package/src/public-cli/run-lifecycle.ts +5 -5
- package/src/public-cli/settlement.ts +38 -11
- package/src/public-cli/terminal.ts +6 -0
- package/src/report-ticket-tool.ts +176 -0
- package/src/role-envelope.ts +28 -3
- package/src/role-run-placement.ts +0 -2
- package/src/role-run-relocation.ts +45 -4
- package/src/role-runtime.ts +6 -0
- package/src/worker-submission-gates.ts +26 -3
|
@@ -1365,8 +1365,7 @@ function roleRunPlacement(ledgerHome, input) {
|
|
|
1365
1365
|
return {
|
|
1366
1366
|
runDirectory,
|
|
1367
1367
|
sessionDirectory,
|
|
1368
|
-
sessionFile: sessionFileIn(sessionDirectory)
|
|
1369
|
-
attachmentsDirectory: join5(runDirectory, "attachments")
|
|
1368
|
+
sessionFile: sessionFileIn(sessionDirectory)
|
|
1370
1369
|
};
|
|
1371
1370
|
}
|
|
1372
1371
|
function isUnboundRunDirectory(runDirectory) {
|
|
@@ -9993,7 +9992,7 @@ function rewriteAdmittedRoleRunPage(page, rewrites) {
|
|
|
9993
9992
|
if (isRecord(page.principal)) {
|
|
9994
9993
|
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
9995
9994
|
page.principal,
|
|
9996
|
-
|
|
9995
|
+
PRINCIPAL_SESSION_PATH_FIELDS,
|
|
9997
9996
|
rewrites
|
|
9998
9997
|
);
|
|
9999
9998
|
}
|
|
@@ -10166,7 +10165,7 @@ async function rewriteRoleRunDurablePages(input) {
|
|
|
10166
10165
|
if (isRecord(page.principal)) {
|
|
10167
10166
|
rewriteRunDirectoryPathFieldsAgainstRewrites(
|
|
10168
10167
|
page.principal,
|
|
10169
|
-
|
|
10168
|
+
PRINCIPAL_SESSION_PATH_FIELDS,
|
|
10170
10169
|
rewrites
|
|
10171
10170
|
);
|
|
10172
10171
|
}
|
|
@@ -10206,7 +10205,7 @@ async function rewriteRoleRunDurablePages(input) {
|
|
|
10206
10205
|
}
|
|
10207
10206
|
await rewriteNestedMachinePathPages(pagesDirectory, rewrites);
|
|
10208
10207
|
}
|
|
10209
|
-
var ADMITTED_PAGE_FIELDS, INVOCATION_PAGE_FIELDS, RUN_STATE_PAGE_FIELDS, SOURCE_RUN_LOCATOR_FIELDS, SUMMONS_PATH_FIELDS, OFFICER_POINTER_FIELDS, SITIAN_RECORD_FIELDS, SESSION_HEADER_FIELDS, BINDING_PARENT_FIELDS;
|
|
10208
|
+
var ADMITTED_PAGE_FIELDS, INVOCATION_PAGE_FIELDS, RUN_STATE_PAGE_FIELDS, PRINCIPAL_SESSION_PATH_FIELDS, SOURCE_RUN_LOCATOR_FIELDS, SUMMONS_PATH_FIELDS, OFFICER_POINTER_FIELDS, SITIAN_RECORD_FIELDS, SESSION_HEADER_FIELDS, BINDING_PARENT_FIELDS;
|
|
10210
10209
|
var init_role_run_relocation = __esm({
|
|
10211
10210
|
"src/role-run-relocation.ts"() {
|
|
10212
10211
|
"use strict";
|
|
@@ -10240,6 +10239,10 @@ var init_role_run_relocation = __esm({
|
|
|
10240
10239
|
"sessionDirectory",
|
|
10241
10240
|
"sessionFile"
|
|
10242
10241
|
];
|
|
10242
|
+
PRINCIPAL_SESSION_PATH_FIELDS = [
|
|
10243
|
+
"sessionDirectory",
|
|
10244
|
+
"sessionFile"
|
|
10245
|
+
];
|
|
10243
10246
|
SOURCE_RUN_LOCATOR_FIELDS = ["runDirectory"];
|
|
10244
10247
|
SUMMONS_PATH_FIELDS = ["sourceRunPath"];
|
|
10245
10248
|
OFFICER_POINTER_FIELDS = ["sessionFile", "runDirectory"];
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { execFile, spawn } from "node:child_process";
|
|
7
7
|
import { constants } from "node:fs";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
8
9
|
import { access, appendFile, realpath } from "node:fs/promises";
|
|
9
10
|
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
10
11
|
import { platform } from "node:process";
|
|
@@ -16,6 +17,8 @@ import { projectActivationFlags } from "../role-activation-flags.js";
|
|
|
16
17
|
import { encodeUserDialogueStdin } from "../user-dialogue-stdin.js";
|
|
17
18
|
import { readLedgerSessionJsonlLines, readStrictPiSessionJsonl } from "../ledger-session-read.js";
|
|
18
19
|
import { copyAndRecordHostDossier } from "../host-session-record.js";
|
|
20
|
+
import { resolveLiveRunDirectoryPath, retainPackageFaultBesideLivePlacement, syncTurnRequestLivePlacement, } from "../external-host-turn-loop.js";
|
|
21
|
+
import { homeFromRunDirectory } from "../activation-ledger-topology.js";
|
|
19
22
|
import { projectThrownFailureLeaf, retainPackageFault } from "../public-cli/settlement.js";
|
|
20
23
|
import { describeErrorIdentity } from "../public-cli/run-lifecycle.js";
|
|
21
24
|
import { RECEIPT_DELIVERY_LIMIT_ENV } from "../receipt-delivery-policy.js";
|
|
@@ -264,7 +267,34 @@ export function createDefaultPiSpawnRunner(options) {
|
|
|
264
267
|
packageErrors.push(stdinDeliveryError);
|
|
265
268
|
for (const error of packageErrors) {
|
|
266
269
|
const diagnostic = `Pi transport handling failed beside host terminal: ${describeErrorIdentity(error)}`;
|
|
267
|
-
|
|
270
|
+
// #1171 F4: spawn-time path may have been renamed; reuse
|
|
271
|
+
// resolveLiveRunDirectoryPath (same authority as syncTurnRequestLivePlacement).
|
|
272
|
+
const spawnDir = spawnOptions.env.AK_ROLE_RUN_DIR;
|
|
273
|
+
let runDirectory;
|
|
274
|
+
if (typeof spawnDir === "string" && spawnDir.trim() !== "") {
|
|
275
|
+
try {
|
|
276
|
+
runDirectory = await resolveLiveRunDirectoryPath(spawnDir, homeFromRunDirectory(spawnDir));
|
|
277
|
+
}
|
|
278
|
+
catch (resolveError) {
|
|
279
|
+
// Resolve threw beside an already-closed child: keep both causes
|
|
280
|
+
// on the existing retain/stderr presentation path (失败诚实).
|
|
281
|
+
runDirectory = existsSync(spawnDir) ? spawnDir : undefined;
|
|
282
|
+
const resolveDiagnostic = `live run path resolve failed beside Pi transport fault: ${describeErrorIdentity(resolveError)}`;
|
|
283
|
+
if (runDirectory !== undefined) {
|
|
284
|
+
await retainPackageFault({
|
|
285
|
+
runDirectory,
|
|
286
|
+
diagnostic: resolveDiagnostic,
|
|
287
|
+
error: resolveError,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
try {
|
|
292
|
+
process.stderr.write(`${resolveDiagnostic}\n`);
|
|
293
|
+
}
|
|
294
|
+
catch { /* Best-effort presentation beside the already closed child. */ }
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
268
298
|
if (runDirectory !== undefined) {
|
|
269
299
|
await retainPackageFault({ runDirectory, diagnostic, error });
|
|
270
300
|
}
|
|
@@ -374,6 +404,7 @@ export function createPiRoleTurnHost(config) {
|
|
|
374
404
|
// bind projects host.sessions; copy records the landing. Host adapters do
|
|
375
405
|
// not write current.json — public bind/settlement seams own that render.
|
|
376
406
|
try {
|
|
407
|
+
await syncTurnRequestLivePlacement(request);
|
|
377
408
|
const { sessionFile, sessionDirectory } = config.principalAuthority.decode(request.principal);
|
|
378
409
|
const sessionId = await readPiSessionHeaderId(sessionFile);
|
|
379
410
|
if (sessionId !== undefined) {
|
|
@@ -389,8 +420,7 @@ export function createPiRoleTurnHost(config) {
|
|
|
389
420
|
}
|
|
390
421
|
}
|
|
391
422
|
catch (error) {
|
|
392
|
-
await
|
|
393
|
-
runDirectory: request.runDirectory,
|
|
423
|
+
await retainPackageFaultBesideLivePlacement(request, {
|
|
394
424
|
diagnostic: `pi host dossier record failed beside host terminal: ${describeErrorIdentity(error)}`,
|
|
395
425
|
error,
|
|
396
426
|
});
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* starts two ordinary single-axis runs here.
|
|
5
5
|
*/
|
|
6
6
|
import { dirname, resolve } from "node:path";
|
|
7
|
+
import { MISSING_TICKET_REASK_MATERIAL } from "../report-ticket-tool.js";
|
|
7
8
|
import { isUnboundRunDirectory, sessionFileOf } from "../role-run-placement.js";
|
|
9
|
+
import { rewriteRunDirectoryPathValue } from "../role-run-relocation.js";
|
|
10
|
+
import { readPackageMaterial } from "../session-opening-materials.js";
|
|
8
11
|
import { bookDirectOfficerRunPointer, readDirectOfficerRunPointer } from "../archivist-record-pointer.js";
|
|
9
12
|
import { createPiDoctorAuditor } from "../doctor-auditor.js";
|
|
10
13
|
import { officerConclusionReask, gateOfficerForSubject } from "../gatekeeper-role.js";
|
|
@@ -21,7 +24,7 @@ import { readRecordedSubmissionRows } from "../submission-ledger.js";
|
|
|
21
24
|
import { summonPublicRole } from "../public-role-summons.js";
|
|
22
25
|
import { sessionFileFromPublicSummon } from "../session-assistant-usage.js";
|
|
23
26
|
import { latestQueuePayload, latestQueueStatus, } from "../submission-gate.js";
|
|
24
|
-
import { isSafePositiveTicketNumber, readBoardTicketNumber } from "../run-ticket-number.js";
|
|
27
|
+
import { isSafePositiveTicketNumber, readBoardTicketNumber, } from "../run-ticket-number.js";
|
|
25
28
|
import { NotarySourceRunError, resolveNotarySourceRunLocator } from "../notary-source-run.js";
|
|
26
29
|
import { packagedAdmitsCountersign, packagedBindsBoardTicket, packagedRebindSourceOnResume, packagedRoleMetadata, } from "../packaged-role-registry.js";
|
|
27
30
|
import { readAuditorResumeBinding } from "../auditor-soul.js";
|
|
@@ -86,11 +89,21 @@ function unreadablePostSubmissionStatus(admitted, terminal) {
|
|
|
86
89
|
* status: the caller presents the volumes already in hand and does not advance
|
|
87
90
|
* review. ADR 0007 audit continues are not this shape.
|
|
88
91
|
*/
|
|
92
|
+
/**
|
|
93
|
+
* Drop turn-scoped soft-reask identity; keep cross-turn budgets and ownership cell.
|
|
94
|
+
* #1171 F2-R5 / F2-R9: ordinary status reask and court continue must not inherit
|
|
95
|
+
* softTicketReaskTurn (that flag only governs the soft-reask turn's own settle).
|
|
96
|
+
*/
|
|
97
|
+
function withoutSoftTicketReaskTurn(env) {
|
|
98
|
+
const { softTicketReaskTurn: _softTicketReaskTurn, ...rest } = env;
|
|
99
|
+
return rest;
|
|
100
|
+
}
|
|
89
101
|
function withUnreadableReask(env) {
|
|
90
102
|
const spent = env.unreadableReasksSpent ?? 0;
|
|
91
103
|
if (spent >= deliveryLimitFromConfig(env.autoResumeLimit))
|
|
92
104
|
return undefined;
|
|
93
|
-
|
|
105
|
+
// Ordinary status reask: strip soft-reask turn identity; keep spent budget (#1171 F2-R9).
|
|
106
|
+
return { ...withoutSoftTicketReaskTurn(env), unreadableReasksSpent: spent + 1 };
|
|
94
107
|
}
|
|
95
108
|
/** Budget spent on an unreadable status. Distinct from "status is readable". */
|
|
96
109
|
const UNREADABLE_REASK_EXHAUSTED = { unreadableReaskExhausted: true };
|
|
@@ -156,6 +169,146 @@ async function reaskUnreadablePostSubmissionStatus(admitted, terminal, env, io,
|
|
|
156
169
|
return UNREADABLE_REASK_EXHAUSTED;
|
|
157
170
|
return runPublicInstructionSeatResume({ runId: admitted.runId, summons: { instruction: message } }, next, io);
|
|
158
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* #1171: sealed submission still leaves the leg unbound → soft reask once.
|
|
174
|
+
* Does not reject the sealed receipt; exhaustion leaves unbound as filed.
|
|
175
|
+
* Reask copy lives in package resources (ADR 0073).
|
|
176
|
+
* #1171 F2-R7: ownership comes from this nested resume's settlement seam
|
|
177
|
+
* (softTicketReaskOwnership.sealedSubstitute set when the soft-reask turn itself seals),
|
|
178
|
+
* not full-leg sealed count / time-window deltas that can absorb another court's seal.
|
|
179
|
+
*/
|
|
180
|
+
async function reaskMissingTicketOnce(admitted, terminal, env, io) {
|
|
181
|
+
if ((env.ticketReasksSpent ?? 0) >= 1)
|
|
182
|
+
return { kind: "none" };
|
|
183
|
+
if (admitted.ticketNumber !== undefined)
|
|
184
|
+
return { kind: "none" };
|
|
185
|
+
if (!isUnboundRunDirectory(admitted.runDirectory))
|
|
186
|
+
return { kind: "none" };
|
|
187
|
+
if (terminal?.roleOutcome.kind !== "accepted" && terminal?.roleOutcome.kind !== "audit_escalation") {
|
|
188
|
+
return { kind: "none" };
|
|
189
|
+
}
|
|
190
|
+
const payloads = terminal.roleOutcome.payloads ?? [];
|
|
191
|
+
if (payloads.length === 0)
|
|
192
|
+
return { kind: "none" };
|
|
193
|
+
// Sealed-with-ticket skips via admitted.ticketNumber after bindSealedTicketNumber
|
|
194
|
+
// (post-admission). Do not re-parse payload.ticketNumber here — that would
|
|
195
|
+
// duplicate the existing ticket seam (#1171 notary: no rule copy).
|
|
196
|
+
const instruction = (await readPackageMaterial(MISSING_TICKET_REASK_MATERIAL)).trim();
|
|
197
|
+
// Spend budget and mark this turn as the soft reask (settlement identity).
|
|
198
|
+
// Ownership cell is set at that turn's settlement; env spreads keep the same
|
|
199
|
+
// ref through audit continue — see envForCourtContinue (#1171 F2-R5 / F2-R7).
|
|
200
|
+
const softTicketReaskOwnership = { sealedSubstitute: false };
|
|
201
|
+
const softReaskEnv = {
|
|
202
|
+
...env,
|
|
203
|
+
ticketReasksSpent: 1,
|
|
204
|
+
softTicketReaskTurn: true,
|
|
205
|
+
softTicketReaskOwnership,
|
|
206
|
+
};
|
|
207
|
+
const result = await runPublicInstructionSeatResume({ runId: admitted.runId, summons: { instruction } }, softReaskEnv, io);
|
|
208
|
+
if (softTicketReaskOwnership.sealedSubstitute
|
|
209
|
+
|| terminalCarriesSealedSubmission(result.terminal)) {
|
|
210
|
+
return { kind: "nested_owns", result };
|
|
211
|
+
}
|
|
212
|
+
return { kind: "no_substitute", result };
|
|
213
|
+
}
|
|
214
|
+
/** Court continue: keep cross-turn budgets + ownership cell; drop turn-scoped soft-reask identity. */
|
|
215
|
+
function envForCourtContinue(env) {
|
|
216
|
+
return { ...withoutSoftTicketReaskTurn(env), unreadableReasksSpent: 0 };
|
|
217
|
+
}
|
|
218
|
+
/** True when a terminal still carries a sealed accepted / audit_escalation volume. */
|
|
219
|
+
function terminalCarriesSealedSubmission(terminal) {
|
|
220
|
+
const kind = terminal?.roleOutcome.kind;
|
|
221
|
+
if (kind !== "accepted" && kind !== "audit_escalation")
|
|
222
|
+
return false;
|
|
223
|
+
return (terminal?.roleOutcome.payloads ?? []).length > 0;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* #1171 F2-R2: kept original terminal may still name pre-relocate artifact
|
|
227
|
+
* paths after a report-only reask moved the leg. Project onto live admitted
|
|
228
|
+
* via the same rewrite seam post-admission uses after relocate.
|
|
229
|
+
*/
|
|
230
|
+
function projectKeptOriginalArtifactsOntoLiveAdmitted(original, kept) {
|
|
231
|
+
const oldRunDirectory = original.admitted?.runDirectory;
|
|
232
|
+
const newRunDirectory = kept.admitted?.runDirectory;
|
|
233
|
+
if (oldRunDirectory === undefined
|
|
234
|
+
|| newRunDirectory === undefined
|
|
235
|
+
|| oldRunDirectory === newRunDirectory
|
|
236
|
+
|| kept.terminal === undefined) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
for (const artifact of kept.terminal.artifacts) {
|
|
240
|
+
artifact.path = rewriteRunDirectoryPathValue(artifact.path, oldRunDirectory, newRunDirectory);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* #1171 F2: missing-ticket reask did not seal a substitute — keep original
|
|
245
|
+
* terminal; take live placement from the reask when it relocated.
|
|
246
|
+
* #1171 F2-R1: silence / report-only stays on the original chain; a real host
|
|
247
|
+
* failure (nonzero exit) must be delivered honestly — do not wash it into the
|
|
248
|
+
* original accepted result. Sealed original volume remains on disk either way.
|
|
249
|
+
* #1171 F2-R2: after keep, project report artifact paths onto the live
|
|
250
|
+
* admitted directory so public result, durable face, and artifact refs agree.
|
|
251
|
+
* Substitute ownership is decided once at the soft-reask call seam (F2-R6);
|
|
252
|
+
* this helper only finishes the keep path.
|
|
253
|
+
*/
|
|
254
|
+
function mergeMissingTicketReaskKeepOriginal(original, ticketReask) {
|
|
255
|
+
if (ticketReask.exitCode !== 0)
|
|
256
|
+
return ticketReask;
|
|
257
|
+
const admitted = ticketReask.admitted ?? original.admitted;
|
|
258
|
+
if (admitted === undefined)
|
|
259
|
+
return { ...original };
|
|
260
|
+
const kept = { ...original, admitted };
|
|
261
|
+
projectKeptOriginalArtifactsOntoLiveAdmitted(original, kept);
|
|
262
|
+
return kept;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Post-submission status reask, then missing-ticket soft reask, then audit.
|
|
266
|
+
* #1171 F3: status exhaustion must not skip the one missing-ticket reask.
|
|
267
|
+
* #1171 F2: ticket reask without a substitute seal continues the original chain.
|
|
268
|
+
*/
|
|
269
|
+
async function continueAfterPostSubmissionGuards(result, admittedRole, env, io, held) {
|
|
270
|
+
const live = result.admitted;
|
|
271
|
+
if (live === undefined) {
|
|
272
|
+
await presentOriginalVolume(held, result.terminal, io);
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
const unreadableReask = await reaskUnreadablePostSubmissionStatus(live, result.terminal, env, io);
|
|
276
|
+
let afterStatus = result;
|
|
277
|
+
if (isUnreadableReaskExhausted(unreadableReask)) {
|
|
278
|
+
const terminal = result.terminal === undefined
|
|
279
|
+
? undefined
|
|
280
|
+
: withUnsettledDirection(result.terminal);
|
|
281
|
+
afterStatus = terminal === undefined
|
|
282
|
+
? { ...result, admitted: live }
|
|
283
|
+
: { ...result, admitted: live, terminal };
|
|
284
|
+
// Fall through: missing-ticket reask still owed on the original sealed volume.
|
|
285
|
+
}
|
|
286
|
+
else if (unreadableReask !== undefined) {
|
|
287
|
+
return unreadableReask;
|
|
288
|
+
}
|
|
289
|
+
const forTicket = afterStatus.admitted ?? live;
|
|
290
|
+
// Ownership is decided once inside reaskMissingTicketOnce (#1171 F2-R6).
|
|
291
|
+
const softReask = await reaskMissingTicketOnce(forTicket, afterStatus.terminal ?? result.terminal, env, io);
|
|
292
|
+
if (softReask.kind === "nested_owns") {
|
|
293
|
+
// Nested resume already finished guards + audit/present for the substitute.
|
|
294
|
+
// Do not run a second outer settlement of the same public call (#1171 F2-R4b).
|
|
295
|
+
return softReask.result;
|
|
296
|
+
}
|
|
297
|
+
const continued = softReask.kind === "none"
|
|
298
|
+
? afterStatus
|
|
299
|
+
: mergeMissingTicketReaskKeepOriginal(afterStatus, softReask.result);
|
|
300
|
+
const continuedAdmitted = continued.admitted ?? live;
|
|
301
|
+
// Spent budget rides the outer audit/continue chain (#1171 F2-R4a). Soft-reask
|
|
302
|
+
// settlement identity does not — ordinary continue turns publish no_receipt (#1171 F2-R5).
|
|
303
|
+
const continuedEnv = softReask.kind !== "none"
|
|
304
|
+
? { ...env, ticketReasksSpent: 1 }
|
|
305
|
+
: env;
|
|
306
|
+
if (AUDITED_ROLES.has(admittedRole) && continued.terminal?.roleOutcome.kind === "accepted") {
|
|
307
|
+
return auditSubmittedRole({ ...continued, admitted: continuedAdmitted }, continuedEnv, io);
|
|
308
|
+
}
|
|
309
|
+
await presentOriginalVolume(held, continued.terminal, io, continuedAdmitted.runDirectory);
|
|
310
|
+
return { ...continued, admitted: continuedAdmitted };
|
|
311
|
+
}
|
|
159
312
|
function roleRecord(role) {
|
|
160
313
|
const record = packagedRoleMetadata(role);
|
|
161
314
|
if (record === undefined) {
|
|
@@ -360,21 +513,9 @@ async function dispatchAdmitted(admitted, env, io) {
|
|
|
360
513
|
});
|
|
361
514
|
};
|
|
362
515
|
const result = await execute(env);
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
? undefined
|
|
367
|
-
: withUnsettledDirection(result.terminal);
|
|
368
|
-
await presentOriginalVolume(held, terminal, io, admitted.runDirectory);
|
|
369
|
-
return terminal === undefined ? result : { ...result, terminal };
|
|
370
|
-
}
|
|
371
|
-
if (unreadableReask !== undefined)
|
|
372
|
-
return unreadableReask;
|
|
373
|
-
if (AUDITED_ROLES.has(admitted.role) && result.terminal?.roleOutcome.kind === "accepted") {
|
|
374
|
-
return auditSubmittedRole(result, env, io);
|
|
375
|
-
}
|
|
376
|
-
await presentOriginalVolume(held, result.terminal, io, admitted.runDirectory);
|
|
377
|
-
return result;
|
|
516
|
+
// Prefer the live admitted object returned from the turn (may have relocated).
|
|
517
|
+
const live = result.admitted ?? admitted;
|
|
518
|
+
return continueAfterPostSubmissionGuards({ ...result, admitted: live }, admitted.role, env, io, held);
|
|
378
519
|
}
|
|
379
520
|
/**
|
|
380
521
|
* Omitted lens on a parallel-lens seat: two ordinary single-axis runs, no parent run.
|
|
@@ -594,10 +735,9 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
|
|
|
594
735
|
const result = await runCountersignBody(parsed, env, {
|
|
595
736
|
stdout: (value) => held.push(value), stderr: io.stderr,
|
|
596
737
|
});
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
return result;
|
|
738
|
+
// Same post-submission guards as every other seat (#1171 F8): missing-ticket
|
|
739
|
+
// soft reask once, then audit — no early return that skips the reask.
|
|
740
|
+
return continueAfterPostSubmissionGuards(result, "countersign", env, io, held);
|
|
601
741
|
}
|
|
602
742
|
if ("parallelLenses" in record && record.parallelLenses === true) {
|
|
603
743
|
if (parsed.baseRevision === undefined) {
|
|
@@ -842,24 +982,11 @@ export async function runPublicInstructionSeatResume(request, env, io) {
|
|
|
842
982
|
},
|
|
843
983
|
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
844
984
|
});
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
if (isUnreadableReaskExhausted(unreadableReask)) {
|
|
849
|
-
const terminal = result.terminal === undefined
|
|
850
|
-
? undefined
|
|
851
|
-
: withUnsettledDirection(result.terminal);
|
|
852
|
-
await presentOriginalVolume(held, terminal, io, result.admitted?.runDirectory);
|
|
853
|
-
return terminal === undefined ? result : { ...result, terminal };
|
|
854
|
-
}
|
|
855
|
-
if (unreadableReask !== undefined)
|
|
856
|
-
return unreadableReask;
|
|
857
|
-
if (result.admitted !== undefined && AUDITED_ROLES.has(result.admitted.role)
|
|
858
|
-
&& result.terminal?.roleOutcome.kind === "accepted") {
|
|
859
|
-
return auditSubmittedRole(result, env, io);
|
|
985
|
+
if (result.admitted === undefined) {
|
|
986
|
+
await presentOriginalVolume(held, result.terminal, io);
|
|
987
|
+
return result;
|
|
860
988
|
}
|
|
861
|
-
|
|
862
|
-
return result;
|
|
989
|
+
return continueAfterPostSubmissionGuards(result, result.admitted.role, env, io, held);
|
|
863
990
|
};
|
|
864
991
|
let binding;
|
|
865
992
|
try {
|
|
@@ -905,7 +1032,7 @@ async function queueConclusionFromChild(child, env, io) {
|
|
|
905
1032
|
// Notary, auditor, and inspector have no seat-local status reask. This loop
|
|
906
1033
|
// is their one budget. The child resume keeps the caller's env so delivery,
|
|
907
1034
|
// failure recovery, and other reask loops stay separate.
|
|
908
|
-
let budgetEnv =
|
|
1035
|
+
let budgetEnv = envForCourtContinue(env);
|
|
909
1036
|
for (;;) {
|
|
910
1037
|
const terminal = await trySettlePublicSeat(current, env.principalAuthority, { ...await readCurrentCourt(current.runDirectory), ...packageFaultScope(current, env, io) });
|
|
911
1038
|
const status = latestQueueStatus(terminal);
|
|
@@ -966,7 +1093,7 @@ export async function continueParentAfterChild(parentRunId, child, env, io) {
|
|
|
966
1093
|
return runPublicInstructionSeatResume({
|
|
967
1094
|
runId: parentRunId,
|
|
968
1095
|
summons: { instruction: readableGateItem(latestQueuePayload(resolved.terminal)) },
|
|
969
|
-
},
|
|
1096
|
+
}, envForCourtContinue(env), io);
|
|
970
1097
|
}
|
|
971
1098
|
const result = await dispatchAdmitted(admitted, env, { ...io, omitFailureStderrDiagnostic: true });
|
|
972
1099
|
showResumeErrorPointer(io, result.exitCode, result.terminal);
|
|
@@ -1005,6 +1132,8 @@ async function officerPassedCurrentSubmission(input) {
|
|
|
1005
1132
|
}
|
|
1006
1133
|
/** Finished submissions enter the existing audit gate after their tool call has returned. */
|
|
1007
1134
|
async function auditSubmittedRole(turn, env, io, resumedOfficer, passedReceipt) {
|
|
1135
|
+
// Ordinary audit chain: drop soft-reask turn identity; keep budgets/ownership (#1171 F2-R9).
|
|
1136
|
+
env = withoutSoftTicketReaskTurn(env);
|
|
1008
1137
|
const admitted = turn.admitted;
|
|
1009
1138
|
if (admitted === undefined || !AUDITED_ROLES.has(admitted.role)
|
|
1010
1139
|
|| turn.terminal?.roleOutcome.kind !== "accepted")
|
|
@@ -1053,11 +1182,13 @@ async function auditSubmittedRole(turn, env, io, resumedOfficer, passedReceipt)
|
|
|
1053
1182
|
await presentTerminal(turn.terminal, { ...io, omitFailureStderrDiagnostic: true }, admitted.runDirectory);
|
|
1054
1183
|
return turn;
|
|
1055
1184
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1185
|
+
// #1171 F2-R8: bind audit to the sealed identity carried on this turn's
|
|
1186
|
+
// settlement terminal — never the whole-leg latest accepted row (another
|
|
1187
|
+
// lawful public call may have sealed and passed台院 meanwhile).
|
|
1188
|
+
const toolCallId = turn.terminal.submissionToolCallId;
|
|
1189
|
+
if (toolCallId === undefined || toolCallId.length === 0) {
|
|
1060
1190
|
throw new Error("accepted submission has no tool call identity");
|
|
1191
|
+
}
|
|
1061
1192
|
const sessionFile = env.principalAuthority.decode(admitted.principal).sessionFile;
|
|
1062
1193
|
const entries = admitted.role === "doctor"
|
|
1063
1194
|
? await readBoundSessionEntries(sessionFile)
|
|
@@ -1118,7 +1249,7 @@ async function auditSubmittedRole(turn, env, io, resumedOfficer, passedReceipt)
|
|
|
1118
1249
|
if (decision.status === "continue") {
|
|
1119
1250
|
return runPublicInstructionSeatResume({
|
|
1120
1251
|
runId: admitted.runId, summons: { instruction: readableGateItem(decision.receipt ?? decision.violations) },
|
|
1121
|
-
},
|
|
1252
|
+
}, envForCourtContinue(env), io);
|
|
1122
1253
|
}
|
|
1123
1254
|
if (decision.status === "received") {
|
|
1124
1255
|
if (lastSummon?.terminal === undefined) {
|
|
@@ -1224,7 +1355,7 @@ async function auditSubmittedRole(turn, env, io, resumedOfficer, passedReceipt)
|
|
|
1224
1355
|
return runPublicInstructionSeatResume({
|
|
1225
1356
|
runId: admitted.runId,
|
|
1226
1357
|
summons: { instruction: readableGateItem(chain.passes.at(-1)?.receipt) },
|
|
1227
|
-
},
|
|
1358
|
+
}, envForCourtContinue(env), io);
|
|
1228
1359
|
}
|
|
1229
1360
|
if (admitted.role === "secretariat") {
|
|
1230
1361
|
const pass = chain.passes.at(-1);
|
|
@@ -11,7 +11,7 @@ import { resolveBookKeyFromGit } from "../activation-ledger-git.js";
|
|
|
11
11
|
import { ensureRoleRunDirectory, ensureRoleRunPlacement, formatRunLeaf, isUnboundRunDirectory, listBookRunDirectories, roleRunPlacement, } from "../role-run-placement.js";
|
|
12
12
|
import { packagedAdmittedSubject, packagedArgvResult, packagedBareToken, packagedBaseIsRevision, packagedEmitsAuthorityRefs, packagedPublicInstructionSubject, packagedRoleMetadata, packagedStoresSourceRunRaw, packagedSubjectChoices, } from "../packaged-role-registry.js";
|
|
13
13
|
import { isSafePositiveTicketNumber, parseTicketNumber, readBoardTicketNumber, requireSafePositiveTicketNumber, } from "../run-ticket-number.js";
|
|
14
|
-
import {
|
|
14
|
+
import { rewriteAdmittedRoleRunPage, } from "../role-run-relocation.js";
|
|
15
15
|
import { readPageSync, renderCurrentSync, updateSectionSync, writeSectionSync } from "../run-dossier.js";
|
|
16
16
|
import { loadDoctorCase, } from "../doctor-evidence.js";
|
|
17
17
|
import { parseCollectorPrNumber, parseCollectorRepository, } from "../collector-config.js";
|
|
@@ -267,13 +267,7 @@ export async function relocateAdmittedRunToTicket(admitted, authority, heldLease
|
|
|
267
267
|
// admitted path ownership immediately after the commit — before any derived
|
|
268
268
|
// render that may refuse (#1161 L1 / BASE order).
|
|
269
269
|
heldLease?.relocate(target.runDirectory);
|
|
270
|
-
|
|
271
|
-
rewriteRunDirectoryPathFields(admittedRecord, [
|
|
272
|
-
"runDirectory",
|
|
273
|
-
"mergerInputPath",
|
|
274
|
-
], oldRunDirectory, target.runDirectory);
|
|
275
|
-
// #1165/#1168: caller file-flag paths stay as given. Obsolete copy fields
|
|
276
|
-
// (taskPath/packetPath) are not rewritten — stock volumes keep their bytes.
|
|
270
|
+
rewriteAdmittedRoleRunPage(admitted, [{ oldRunDirectory, newRunDirectory: target.runDirectory }]);
|
|
277
271
|
admitted.principal = principal;
|
|
278
272
|
// current.json projects host.original from this run directory. Ownership is
|
|
279
273
|
// already transferred; a refuse still carries the true cause to the caller
|
|
@@ -969,7 +963,7 @@ async function placeRoleAdmission(options) {
|
|
|
969
963
|
const projectRoot = resolve(options.project ?? options.cwd);
|
|
970
964
|
const runId = options.runId ?? (options.createRunId ?? uuidv7)();
|
|
971
965
|
const ticketFields = ticketAdmissionFields(options.assertedTicketNumber);
|
|
972
|
-
const { principal, sessionDirectory, sessionFile, runDirectory,
|
|
966
|
+
const { principal, sessionDirectory, sessionFile, runDirectory, ledgerHome, bookKey, } = issueAdmissionPlacement(options.principalAuthority, {
|
|
973
967
|
cwd: projectRoot,
|
|
974
968
|
runId,
|
|
975
969
|
role: options.role,
|
|
@@ -989,7 +983,6 @@ async function placeRoleAdmission(options) {
|
|
|
989
983
|
sessionDirectory,
|
|
990
984
|
sessionFile,
|
|
991
985
|
attachments,
|
|
992
|
-
attachmentsDirectory,
|
|
993
986
|
ledgerHome,
|
|
994
987
|
ticketFields,
|
|
995
988
|
};
|
|
@@ -1009,8 +1002,8 @@ async function persistPlacedAdmission(admitted, placed, model) {
|
|
|
1009
1002
|
}, admitted.role, model);
|
|
1010
1003
|
}
|
|
1011
1004
|
/**
|
|
1012
|
-
* Shared admission: project check, placement,
|
|
1013
|
-
* admitted
|
|
1005
|
+
* Shared admission: project check, placement, admitted and invocation section write.
|
|
1006
|
+
* Caller file-flag paths are recorded opaque on the admitted page (ADR 0087).
|
|
1014
1007
|
* Countersign passes deferPersistence so same-ticket lookup can reserve coordinates
|
|
1015
1008
|
* before materializeCountersignInvocation writes the page.
|
|
1016
1009
|
* Seats whose extra facts are known before placement pass them as admittedFields.
|