@akagilnc/pi-workflow-roles 0.1.3685 → 0.1.3700
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 +499 -559
- package/dist/analyst-gate-cycles-read.js +11 -1
- package/dist/archivist-record-entry.js +1 -1
- package/dist/auditor-dossier-tool.js +12 -0
- package/dist/dossier-resolution.js +0 -35
- package/dist/gatekeeper-pass-envelope.js +39 -34
- package/dist/gatekeeper-role.js +70 -77
- package/dist/judge-auditor.js +5 -24
- package/dist/judge-role.js +39 -42
- package/dist/notary-contracts.js +2 -17
- package/dist/notary-role.js +3 -6
- package/dist/package-contracts/judge-output.js +0 -2
- package/dist/package-contracts/terminating-tools.js +2 -2
- package/dist/public-cli/inspector-run.js +16 -2
- package/dist/public-cli/instruction-seat-run.js +14 -2
- package/dist/public-cli/main.js +189 -214
- package/dist/public-cli/notary-run.js +23 -20
- package/dist/public-cli/post-admission.js +30 -23
- package/dist/public-cli/run-lifecycle.js +6 -14
- package/dist/public-role-summons.js +38 -11
- package/dist/role-runtime.js +28 -3
- package/dist/submission-correctable-error.js +2 -1
- package/dist/submission-errors.js +26 -11
- package/dist/submission-ledger.js +7 -23
- package/extensions/role-runtime.ts +0 -2
- package/package.json +1 -1
- package/souls/countersign.md +3 -1
- package/souls/notary.md +5 -0
- package/src/acp-host/production-host.ts +1 -4
- package/src/acp-host/role-envelope.ts +1 -1
- package/src/analyst-gate-cycles-read.ts +10 -1
- package/src/archivist-record-entry.ts +7 -3
- package/src/auditor-dossier-tool.ts +21 -0
- package/src/dossier-resolution.ts +0 -39
- package/src/gatekeeper-pass-envelope.ts +59 -43
- package/src/gatekeeper-role.ts +114 -113
- package/src/host-contracts.ts +3 -3
- package/src/judge-auditor.ts +5 -43
- package/src/judge-role.ts +41 -54
- package/src/notary-contracts.ts +8 -27
- package/src/notary-role.ts +2 -7
- package/src/package-contracts/judge-output.ts +0 -1
- package/src/package-contracts/terminating-tools.ts +2 -2
- package/src/public-cli/inspector-run.ts +32 -2
- package/src/public-cli/instruction-seat-run.ts +30 -2
- package/src/public-cli/notary-run.ts +48 -16
- package/src/public-cli/post-admission.ts +31 -37
- package/src/public-cli/run-lifecycle.ts +6 -17
- package/src/public-role-summons.ts +76 -12
- package/src/role-runtime.ts +34 -9
- package/src/submission-correctable-error.ts +2 -0
- package/src/submission-errors.ts +28 -11
- package/src/submission-ledger.ts +8 -32
- package/dist/shape-unreadable-failure.js +0 -34
|
@@ -53,6 +53,19 @@ export type PublicSummonRequest = {
|
|
|
53
53
|
* parent tool call is cancelled (#675).
|
|
54
54
|
*/
|
|
55
55
|
readonly signal?: AbortSignal;
|
|
56
|
+
/**
|
|
57
|
+
* #753 plain-language re-ask when the prior officer reply was not three-state.
|
|
58
|
+
* Nested gate officers (notary + inspector) — rides same-ticket resume
|
|
59
|
+
* summons.instruction. Never folded into argv / parent-run lookup keys.
|
|
60
|
+
*/
|
|
61
|
+
readonly reviewReask?: string;
|
|
62
|
+
/**
|
|
63
|
+
* #753/#750 same-parent re-summons: human-readable pointer materials for the
|
|
64
|
+
* new parent submission (source run + optional gate-submission-candidate).
|
|
65
|
+
* Rides summons.instruction when reviewReask is absent. Fresh mint ignores it.
|
|
66
|
+
* Never folded into argv / parent-run lookup keys.
|
|
67
|
+
*/
|
|
68
|
+
readonly gateReviewInstruction?: string;
|
|
56
69
|
};
|
|
57
70
|
|
|
58
71
|
export type PublicSummonResult = {
|
|
@@ -278,6 +291,12 @@ export async function summonPublicRole(
|
|
|
278
291
|
: { autoResumeLimit: config.autoResumeLimit }),
|
|
279
292
|
// Parent cancellation reaches the nested activation's own turn dispatch.
|
|
280
293
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
294
|
+
// #753: reask rides the existing notary same-ticket resume summons.instruction.
|
|
295
|
+
...(options.reviewReask === undefined ? {} : { reviewReask: options.reviewReask }),
|
|
296
|
+
// #753/#750: new-submission pointers on same-parent resume (not conclusion re-ask).
|
|
297
|
+
...(options.gateReviewInstruction === undefined
|
|
298
|
+
? {}
|
|
299
|
+
: { gateReviewInstruction: options.gateReviewInstruction }),
|
|
281
300
|
};
|
|
282
301
|
const captured = options.io === undefined ? createCapturingIo() : undefined;
|
|
283
302
|
const io = options.io ?? captured!.io;
|
|
@@ -376,9 +395,9 @@ export async function summonPublicRole(
|
|
|
376
395
|
};
|
|
377
396
|
}
|
|
378
397
|
|
|
379
|
-
/** Gate officer summons: notary via --source-run; inspector via pointer instruction. */
|
|
398
|
+
/** Gate officer summons: notary/auditor via --source-run; inspector via pointer instruction. */
|
|
380
399
|
export async function summonGateOfficer(options: {
|
|
381
|
-
readonly officer: "inspector" | "notary";
|
|
400
|
+
readonly officer: "inspector" | "notary" | "auditor";
|
|
382
401
|
readonly sourceRunDirectory: string;
|
|
383
402
|
readonly cwd: string;
|
|
384
403
|
readonly home?: string;
|
|
@@ -386,6 +405,17 @@ export async function summonGateOfficer(options: {
|
|
|
386
405
|
readonly io?: CliIo;
|
|
387
406
|
/** Parent cancellation for the nested officer activation (#675). */
|
|
388
407
|
readonly signal?: AbortSignal;
|
|
408
|
+
/**
|
|
409
|
+
* Plain-language re-ask when the prior officer reply was not three-state (#753 / #756).
|
|
410
|
+
* All three officers: reviewReask → same-ticket resume summons.instruction.
|
|
411
|
+
* Never concatenated into argv (inspector parentRunPath is the pure 卷宗指针).
|
|
412
|
+
*/
|
|
413
|
+
readonly reask?: string;
|
|
414
|
+
/**
|
|
415
|
+
* Path of this turn's gate-submission-candidate when written (#632 / #753).
|
|
416
|
+
* Becomes human-readable resume materials when reask is absent.
|
|
417
|
+
*/
|
|
418
|
+
readonly submissionCandidatePath?: string;
|
|
389
419
|
}): Promise<PublicSummonResult> {
|
|
390
420
|
let home = options.home;
|
|
391
421
|
if (home === undefined) {
|
|
@@ -393,24 +423,58 @@ export async function summonGateOfficer(options: {
|
|
|
393
423
|
// Hard path resolve: fail loud — never fall through to packageMachineHome (#604 / #675).
|
|
394
424
|
home = homeFromRunDirectory(options.sourceRunDirectory);
|
|
395
425
|
}
|
|
426
|
+
// Conclusion re-ask keeps sole ownership of reviewReask. New-submission materials
|
|
427
|
+
// ride a separate field so first mint never fails the "reask requires prior" gate.
|
|
428
|
+
let gateReviewInstruction: string | undefined;
|
|
429
|
+
if (options.reask === undefined) {
|
|
430
|
+
const { buildGateOfficerReviewInstruction } = await import("./auditor-dossier-tool.ts");
|
|
431
|
+
gateReviewInstruction = buildGateOfficerReviewInstruction({
|
|
432
|
+
sourceRunDirectory: options.sourceRunDirectory,
|
|
433
|
+
...(options.submissionCandidatePath === undefined
|
|
434
|
+
? {}
|
|
435
|
+
: { submissionCandidatePath: options.submissionCandidatePath }),
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
const common = {
|
|
439
|
+
cwd: options.cwd,
|
|
440
|
+
...(home === undefined ? {} : { home }),
|
|
441
|
+
...(options.packageRoot === undefined ? {} : { packageRoot: options.packageRoot }),
|
|
442
|
+
...(options.io === undefined ? {} : { io: options.io }),
|
|
443
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
444
|
+
...(options.reask === undefined ? {} : { reviewReask: options.reask }),
|
|
445
|
+
...(gateReviewInstruction === undefined
|
|
446
|
+
? {}
|
|
447
|
+
: { gateReviewInstruction }),
|
|
448
|
+
} as const;
|
|
396
449
|
if (options.officer === "notary") {
|
|
450
|
+
// #753: reask rides summonPublicRole → runPublicNotary summons.instruction
|
|
451
|
+
// (same resume seam as ordinary same-parent re-summons — no parallel stack).
|
|
397
452
|
return summonPublicRole({
|
|
398
453
|
role: "notary",
|
|
399
454
|
argv: ["--source-run", options.sourceRunDirectory, "--project", options.cwd],
|
|
400
|
-
|
|
401
|
-
...(home === undefined ? {} : { home }),
|
|
402
|
-
...(options.packageRoot === undefined ? {} : { packageRoot: options.packageRoot }),
|
|
403
|
-
...(options.io === undefined ? {} : { io: options.io }),
|
|
404
|
-
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
455
|
+
...common,
|
|
405
456
|
});
|
|
406
457
|
}
|
|
458
|
+
if (options.officer === "auditor") {
|
|
459
|
+
// #756: judge compliance path — same queue law as notary/inspector.
|
|
460
|
+
// Subject is judge (doctor compliance stays on the disposeCompliance path).
|
|
461
|
+
const { AUDITOR_DOSSIER_PROMPT } = await import("./compliance-transport.ts");
|
|
462
|
+
return summonPublicRole({
|
|
463
|
+
role: "auditor",
|
|
464
|
+
argv: [
|
|
465
|
+
"--subject",
|
|
466
|
+
"judge",
|
|
467
|
+
"--source-run",
|
|
468
|
+
options.sourceRunDirectory,
|
|
469
|
+
AUDITOR_DOSSIER_PROMPT,
|
|
470
|
+
],
|
|
471
|
+
...common,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
// Inspector: argv stays pure 卷宗指针 (#747 parentRunPath); reask rides env only.
|
|
407
475
|
return summonPublicRole({
|
|
408
476
|
role: "inspector",
|
|
409
477
|
argv: [`卷宗指针:${options.sourceRunDirectory}`],
|
|
410
|
-
|
|
411
|
-
...(home === undefined ? {} : { home }),
|
|
412
|
-
...(options.packageRoot === undefined ? {} : { packageRoot: options.packageRoot }),
|
|
413
|
-
...(options.io === undefined ? {} : { io: options.io }),
|
|
414
|
-
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
478
|
+
...common,
|
|
415
479
|
});
|
|
416
480
|
}
|
package/src/role-runtime.ts
CHANGED
|
@@ -116,7 +116,6 @@ import { PACKAGED_ROLE_REGISTRY, packagedRoleMetadata, packagedRoleOutputTool, p
|
|
|
116
116
|
import { isAuditEscalationProjection } from "./audit-escalation.ts";
|
|
117
117
|
import {
|
|
118
118
|
createJudgeRoleRuntime,
|
|
119
|
-
type SoulAuditResult,
|
|
120
119
|
} from "./judge-role.ts";
|
|
121
120
|
import {
|
|
122
121
|
createReviewerRoleRuntime,
|
|
@@ -364,7 +363,9 @@ export {
|
|
|
364
363
|
createGatekeeperOutputTool,
|
|
365
364
|
runGatekeeper,
|
|
366
365
|
} from "./gatekeeper-role.ts";
|
|
367
|
-
export type { GatekeeperResult, GatekeeperSubject, GatekeeperNonPassResult, RunGatekeeperOptions } from "./gatekeeper-role.ts";
|
|
366
|
+
export type { GatekeeperResult, GatekeeperSubject, GatekeeperNonPassResult, GateOfficer, RunGatekeeperOptions } from "./gatekeeper-role.ts";
|
|
367
|
+
export { gateOfficerForSubject } from "./gatekeeper-role.ts";
|
|
368
|
+
import { ParentQueueReaskError } from "./submission-errors.ts";
|
|
368
369
|
|
|
369
370
|
export {
|
|
370
371
|
DOCTOR_EVIDENCE_TOOL_NAME,
|
|
@@ -376,7 +377,6 @@ export { loadDoctorCase } from "./doctor-evidence.ts";
|
|
|
376
377
|
export {
|
|
377
378
|
JUDGE_OUTPUT_TOOL_NAME,
|
|
378
379
|
type JudgeVerdict,
|
|
379
|
-
type SoulAuditResult,
|
|
380
380
|
} from "./judge-role.ts";
|
|
381
381
|
export { ENGINE_DETOUR_TOOL_NAME, AK_ROLE_ENGINE_ENV } from "./engine-detour.ts";
|
|
382
382
|
export {
|
|
@@ -410,7 +410,7 @@ export {
|
|
|
410
410
|
resolveAuditorSubject,
|
|
411
411
|
} from "./auditor-soul.ts";
|
|
412
412
|
export type { AuditorSoulRole } from "./auditor-soul.ts";
|
|
413
|
-
export { JUDGE_AUDIT_TOOL_NAME, SOUL_AUDIT_TOOL_NAME
|
|
413
|
+
export { JUDGE_AUDIT_TOOL_NAME, SOUL_AUDIT_TOOL_NAME } from "./judge-auditor.ts";
|
|
414
414
|
export { DOCTOR_AUDIT_TOOL_NAME, createPiDoctorAuditor } from "./doctor-auditor.ts";
|
|
415
415
|
export type { ComplianceDecision } from "./compliance-transport.ts";
|
|
416
416
|
export {
|
|
@@ -651,9 +651,6 @@ export type RoleRuntimeDependencies = {
|
|
|
651
651
|
options: { context: HostContext; signal?: AbortSignal },
|
|
652
652
|
): Promise<ReviewerDispatchRunResult>;
|
|
653
653
|
shutdownReviewerAgent?(): Promise<void>;
|
|
654
|
-
auditSoulCompliance(
|
|
655
|
-
options: { context: HostContext; signal?: AbortSignal },
|
|
656
|
-
): Promise<SoulAuditResult>;
|
|
657
654
|
activationClock?(): string;
|
|
658
655
|
activationTraceWriter?: (record: ActivationTraceRecord) => void | Promise<void>;
|
|
659
656
|
/** Wall-clock ISO timestamps for tool-execution observation records; defaults to activationClock/Date. */
|
|
@@ -1090,6 +1087,16 @@ function loadFrozenDiaristCatalog(
|
|
|
1090
1087
|
return loadDiaristSourceCatalog(raw);
|
|
1091
1088
|
}
|
|
1092
1089
|
|
|
1090
|
+
/** Countersign status words the queue reads (#753). */
|
|
1091
|
+
const COUNTERSIGN_QUEUE_STATUSES = new Set(["converged", "continue", "escalate"]);
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* Plain-language re-ask when countersignStatus is not a known queue word.
|
|
1095
|
+
* Back to countersign itself — notary is not summoned (#753).
|
|
1096
|
+
*/
|
|
1097
|
+
const COUNTERSIGN_STATUS_REASK =
|
|
1098
|
+
"countersignStatus 不是 converged、continue、escalate 三态之一。请重新交卷,status 写明其一。" as const;
|
|
1099
|
+
|
|
1093
1100
|
export function createCountersignRoleRuntime(
|
|
1094
1101
|
roleHost: RoleHost,
|
|
1095
1102
|
dependencies: CountersignRuntimeDependencies,
|
|
@@ -1097,9 +1104,28 @@ export function createCountersignRoleRuntime(
|
|
|
1097
1104
|
) {
|
|
1098
1105
|
// Notary inner gate difference only — lifecycle stays on the shared envelope.
|
|
1099
1106
|
// Pointer-only summons: officer self-fetches from run dossier (#632 / ADR 0079).
|
|
1107
|
+
// #753 queue: read countersignStatus only — escalate skips gate (thrown to caller);
|
|
1108
|
+
// unreadable status returns to countersign; else notary inner gate.
|
|
1100
1109
|
const beforeAccept: FiledOfficerBeforeAccept | undefined =
|
|
1101
1110
|
hostActions !== undefined && roleHost.requireGatekeeperPass !== undefined
|
|
1102
|
-
? async ({ toolCallId, signal, ctx }) => {
|
|
1111
|
+
? async ({ toolCallId, parameters, signal, ctx }) => {
|
|
1112
|
+
const record =
|
|
1113
|
+
parameters !== null && typeof parameters === "object" && !Array.isArray(parameters)
|
|
1114
|
+
? (parameters as Record<string, unknown>)
|
|
1115
|
+
: undefined;
|
|
1116
|
+
const status =
|
|
1117
|
+
record !== undefined && typeof record.countersignStatus === "string"
|
|
1118
|
+
? record.countersignStatus
|
|
1119
|
+
: undefined;
|
|
1120
|
+
if (status === undefined || !COUNTERSIGN_QUEUE_STATUSES.has(status)) {
|
|
1121
|
+
// Parent status unreadable → back to countersign itself; do not summon notary,
|
|
1122
|
+
// do not forge an officer bounce face (#753).
|
|
1123
|
+
throw new ParentQueueReaskError(COUNTERSIGN_STATUS_REASK);
|
|
1124
|
+
}
|
|
1125
|
+
if (status === "escalate") {
|
|
1126
|
+
// Parent escalate → throw to caller as-is; notary does not attend (#753).
|
|
1127
|
+
return undefined;
|
|
1128
|
+
}
|
|
1103
1129
|
await roleHost.requireGatekeeperPass!({
|
|
1104
1130
|
context: ctx,
|
|
1105
1131
|
subject: { kind: "countersign_verdict" },
|
|
@@ -1483,7 +1509,6 @@ export function createRoleRuntimeExtension(
|
|
|
1483
1509
|
roleHost,
|
|
1484
1510
|
{
|
|
1485
1511
|
loadSoul: dependencies.loadJudgeSoul,
|
|
1486
|
-
auditSoulCompliance: dependencies.auditSoulCompliance,
|
|
1487
1512
|
},
|
|
1488
1513
|
hostActions,
|
|
1489
1514
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
GatekeeperDecisionError,
|
|
3
|
+
ParentQueueReaskError,
|
|
3
4
|
WorkerCommitReminderError,
|
|
4
5
|
WorkerPrefixReminderError,
|
|
5
6
|
WorkerUnfinishedReasonReminderError,
|
|
@@ -25,6 +26,7 @@ export function isCorrectableExecuteError(error: unknown): boolean {
|
|
|
25
26
|
return (
|
|
26
27
|
isCorrectableSubmissionError(error)
|
|
27
28
|
|| error instanceof GatekeeperDecisionError
|
|
29
|
+
|| error instanceof ParentQueueReaskError
|
|
28
30
|
|| error instanceof WorkerCommitReminderError
|
|
29
31
|
|| error instanceof WorkerPrefixReminderError
|
|
30
32
|
|| error instanceof WorkerUnfinishedReasonReminderError
|
package/src/submission-errors.ts
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import type { GatekeeperNonPassResult } from "./gatekeeper-role.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { readableGateItem } from "./readable-gate-item.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tool-result text the parent model sees (#753 / #750 evidence).
|
|
6
|
+
* bounce | escalate → officer receipt verbatim (JSON when structured).
|
|
7
|
+
* no_receipt → honest lifecycle fact. No findings rewrite, no「(无 findings)」.
|
|
8
|
+
* #775: structured field content via readableGateItem (DRY with other gate seams).
|
|
9
|
+
*/
|
|
10
|
+
function serializeReceipt(receipt: unknown): string {
|
|
11
|
+
return readableGateItem(receipt);
|
|
12
|
+
}
|
|
3
13
|
|
|
4
14
|
function gatekeeperNonPassMessage(result: GatekeeperNonPassResult): string {
|
|
5
|
-
if (result.status === "bounce") {
|
|
6
|
-
|
|
7
|
-
? "(无 findings)"
|
|
8
|
-
: joinReadableGateItems(result.findings);
|
|
9
|
-
return `门下省打回重写,findings:${findings}`;
|
|
10
|
-
}
|
|
11
|
-
if (result.status === "unreadable") {
|
|
12
|
-
return `门下省官回执形状不可读(${result.officer}):${result.reason}`;
|
|
15
|
+
if (result.status === "bounce" || result.status === "escalate") {
|
|
16
|
+
return serializeReceipt(result.receipt);
|
|
13
17
|
}
|
|
18
|
+
// no_receipt
|
|
14
19
|
return `门下省 ${result.status}(${result.stage}):${result.reason}`;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
/** Structured non-pass; `.result` is session-projected via tool_result, message feeds the model. */
|
|
18
23
|
export class GatekeeperDecisionError extends Error {
|
|
19
24
|
readonly result: GatekeeperNonPassResult;
|
|
20
|
-
constructor(result: GatekeeperNonPassResult) {
|
|
21
|
-
super(gatekeeperNonPassMessage(result));
|
|
25
|
+
constructor(result: GatekeeperNonPassResult, message?: string) {
|
|
26
|
+
super(message ?? gatekeeperNonPassMessage(result));
|
|
22
27
|
this.name = "GatekeeperDecisionError";
|
|
23
28
|
this.result = result;
|
|
24
29
|
}
|
|
@@ -47,3 +52,15 @@ export class WorkerUnfinishedReasonReminderError extends Error {
|
|
|
47
52
|
this.name = "WorkerUnfinishedReasonReminderError";
|
|
48
53
|
}
|
|
49
54
|
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Parent seat status unreadable for queueing (#753).
|
|
58
|
+
* Correctable back to the parent itself — not a gate officer bounce, no officer field.
|
|
59
|
+
*/
|
|
60
|
+
export class ParentQueueReaskError extends Error {
|
|
61
|
+
readonly code = "parent_queue_reask" as const;
|
|
62
|
+
constructor(message: string) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.name = "ParentQueueReaskError";
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/submission-ledger.ts
CHANGED
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
tryHomeFromAkRolesPath,
|
|
4
4
|
} from "./activation-ledger-topology.ts";
|
|
5
5
|
import type { HostContext, HostToolResult, RoleHost } from "./host-contracts.ts";
|
|
6
|
-
import { isAuditEscalationProjection
|
|
7
|
-
|
|
6
|
+
import { isAuditEscalationProjection } from "./audit-escalation.ts";
|
|
7
|
+
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
acceptedFacts,
|
|
@@ -358,18 +358,6 @@ export function createSubmissionLedgerHost(
|
|
|
358
358
|
// correctable-rejection path as execute-thrown rejections below. The
|
|
359
359
|
// candidate is booked first so the bounced attempt stays observable.
|
|
360
360
|
append({ type: "candidate", attemptId, toolCallId, toolName: tool.name, sequence: ++state.sequence });
|
|
361
|
-
const projectOfficerEscalation = (
|
|
362
|
-
officer: "inspector" | "notary",
|
|
363
|
-
original: Record<string, unknown>,
|
|
364
|
-
deliveredOutput: Record<string, unknown> = original,
|
|
365
|
-
): HostToolResult<unknown> => projectAuditEscalation({
|
|
366
|
-
...original,
|
|
367
|
-
status: "escalate",
|
|
368
|
-
officer,
|
|
369
|
-
reason: original.reason === undefined
|
|
370
|
-
? `门下省${officer}上呈(原卷未附 reason)`
|
|
371
|
-
: original.reason,
|
|
372
|
-
}, deliveredOutput);
|
|
373
361
|
let result: HostToolResult<unknown>;
|
|
374
362
|
try {
|
|
375
363
|
failOnInfrastructureFailureDeclaration(
|
|
@@ -385,14 +373,9 @@ export function createSubmissionLedgerHost(
|
|
|
385
373
|
);
|
|
386
374
|
result = await tool.execute(toolCallId, params, signal, update, context);
|
|
387
375
|
} catch (error) {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
error.gatekeeper.officer,
|
|
392
|
-
decision,
|
|
393
|
-
params as Record<string, unknown>,
|
|
394
|
-
);
|
|
395
|
-
} else if (isCorrectableExecuteError(error)) {
|
|
376
|
+
// #753: gate no longer throws GatekeeperEscalationError to select parent
|
|
377
|
+
// next-step. Officer bounce|escalate returns as correctable with raw receipt.
|
|
378
|
+
if (isCorrectableExecuteError(error)) {
|
|
396
379
|
append({
|
|
397
380
|
type: "outcome",
|
|
398
381
|
attemptId,
|
|
@@ -413,16 +396,9 @@ export function createSubmissionLedgerHost(
|
|
|
413
396
|
throw error;
|
|
414
397
|
}
|
|
415
398
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
result.details !== null &&
|
|
420
|
-
!Array.isArray(result.details) &&
|
|
421
|
-
(result.details as Record<string, unknown>).status === "escalate"
|
|
422
|
-
) {
|
|
423
|
-
const details = result.details as Record<string, unknown>;
|
|
424
|
-
result = projectOfficerEscalation(role, details);
|
|
425
|
-
}
|
|
399
|
+
// #753 escalate-thrown-verbatim: officer escalate seals as accepted with the
|
|
400
|
+
// raw receipt — no projectOfficerEscalation rewrite, no fabricated reason.
|
|
401
|
+
// Compliance seats still return live audit_escalation projections below.
|
|
426
402
|
if (isAuditEscalationProjection(result.details)) {
|
|
427
403
|
const candidates = rounds.get(attemptId) ?? [];
|
|
428
404
|
candidates.push({
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Single authoritative shape-unreadable marker (#675 / ADR 0055).
|
|
3
|
-
* Settlement stamps it; consumers only read it — never re-derive from cause=output.
|
|
4
|
-
*/
|
|
5
|
-
export const SHAPE_UNREADABLE_KEY = "shapeUnreadable";
|
|
6
|
-
function isRecord(value) {
|
|
7
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
-
}
|
|
9
|
-
/** Settlement seat path: stamp retained candidate as shape-unreadable (not infrastructure). */
|
|
10
|
-
export function stampShapeUnreadableDetails(candidate) {
|
|
11
|
-
return {
|
|
12
|
-
[SHAPE_UNREADABLE_KEY]: true,
|
|
13
|
-
candidate,
|
|
14
|
-
acceptedReceipt: false,
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Read the settlement-stamped shape candidate from a failure decisiveFacts payload.
|
|
19
|
-
* Looks at root and secondaryEvidence only for the typed marker — no cause inference.
|
|
20
|
-
*/
|
|
21
|
-
export function retainedShapeUnreadable(decisiveFacts) {
|
|
22
|
-
if (!isRecord(decisiveFacts))
|
|
23
|
-
return undefined;
|
|
24
|
-
if (decisiveFacts[SHAPE_UNREADABLE_KEY] === true) {
|
|
25
|
-
return { candidate: decisiveFacts.candidate };
|
|
26
|
-
}
|
|
27
|
-
const secondary = isRecord(decisiveFacts.secondaryEvidence)
|
|
28
|
-
? decisiveFacts.secondaryEvidence
|
|
29
|
-
: undefined;
|
|
30
|
-
if (secondary !== undefined && secondary[SHAPE_UNREADABLE_KEY] === true) {
|
|
31
|
-
return { candidate: secondary.candidate };
|
|
32
|
-
}
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|