@akagilnc/pi-workflow-roles 0.1.5066 → 0.1.5079
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 +82 -83
- package/dist/compliance-transport.js +3 -2
- package/dist/countersign-contracts.js +2 -2
- package/dist/gatekeeper-role.js +12 -19
- package/dist/headless-host/production-host.js +82 -83
- package/dist/inspector-contracts.js +5 -2
- package/dist/migrate-book-topology.js +32 -20
- package/dist/open-tool-schema.js +4 -1
- package/dist/package-contracts/judge-output.js +2 -2
- package/dist/package-contracts/terminating-infrastructure.js +4 -1
- package/dist/public-cli/main.js +33 -20
- package/dist/review-submission.js +19 -8
- package/dist/submission-errors.js +17 -0
- package/dist/submission-ledger.js +18 -6
- package/package.json +1 -1
- package/souls/countersign.md +2 -2
- package/souls/ticket-law.md +1 -1
- package/src/compliance-transport.ts +3 -2
- package/src/countersign-contracts.ts +2 -2
- package/src/countersign-role.ts +3 -7
- package/src/gatekeeper-role.ts +14 -18
- package/src/inspector-contracts.ts +5 -2
- package/src/judge-role.ts +13 -21
- package/src/open-tool-schema.ts +9 -4
- package/src/package-contracts/judge-output.ts +2 -4
- package/src/package-contracts/terminating-infrastructure.ts +9 -4
- package/src/review-submission.ts +24 -10
- package/src/role-runtime.ts +9 -25
- package/src/submission-errors.ts +16 -0
- package/src/submission-gate.ts +2 -1
- package/src/submission-ledger.ts +23 -11
- package/src/worker-role.ts +4 -6
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
/** One shared terminating receipt contract for review officers (#1028). */
|
|
1
|
+
/** One shared terminating receipt contract for review officers (#1028 / #1055). */
|
|
2
2
|
import { Type } from "typebox";
|
|
3
|
-
import { openToolObject } from "./open-tool-schema.js";
|
|
4
3
|
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.js";
|
|
5
4
|
export const REVIEW_SUBMISSION_OUTPUT_TOOL_NAME = "ak_submission_output";
|
|
6
|
-
/**
|
|
7
|
-
export const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
/** Single schema copy of the review queue words. Runtime membership reads this set. */
|
|
6
|
+
export const REVIEW_QUEUE_WORDS = ["converged", "continue", "escalate"];
|
|
7
|
+
export const REVIEW_QUEUE_STATUSES = new Set(REVIEW_QUEUE_WORDS);
|
|
8
|
+
/**
|
|
9
|
+
* One object root. Codex structured output rejects a root anyOf, and status
|
|
10
|
+
* stays a required three-state enum with no omitted-status failure branch.
|
|
11
|
+
* A review seat that can still submit records a real external-dependency
|
|
12
|
+
* failure as escalate plus the existing infrastructureFailure field.
|
|
13
|
+
*/
|
|
14
|
+
export const reviewSubmissionSchema = withTerminatingOutputDeclarations(Type.Object({
|
|
15
|
+
status: Type.Union([
|
|
16
|
+
Type.Literal(REVIEW_QUEUE_WORDS[0]),
|
|
17
|
+
Type.Literal(REVIEW_QUEUE_WORDS[1]),
|
|
18
|
+
Type.Literal(REVIEW_QUEUE_WORDS[2]),
|
|
19
|
+
], {
|
|
20
|
+
description: "审核队列三态枚举,必填。审核席仍能交卷而完成审核所需的外部依赖发生真实基础设施失败时,status 取 escalate,细节写入 infrastructureFailure。不得省略 status,不得用另两个枚举值充当放行或封驳。包侧不因格式拒收。",
|
|
21
|
+
}),
|
|
11
22
|
findings: Type.Optional(Type.Unknown({ description: "审核发现,原样留存" })),
|
|
12
23
|
reason: Type.Optional(Type.Unknown({ description: "上呈理由" })),
|
|
13
24
|
violations: Type.Optional(Type.Unknown({ description: "违规条目" })),
|
|
@@ -17,4 +28,4 @@ export const reviewSubmissionSchema = withTerminatingOutputDeclarations(openTool
|
|
|
17
28
|
note: Type.Optional(Type.Unknown({ description: "裁决附注" })),
|
|
18
29
|
evidence: Type.Optional(Type.Unknown({ description: "留存证据" })),
|
|
19
30
|
decisionGate: Type.Optional(Type.Unknown({ description: "需人权威处置的问题与选项;可说明 question、options,原样留存" })),
|
|
20
|
-
}))
|
|
31
|
+
}, { additionalProperties: true }));
|
|
@@ -50,6 +50,23 @@ export class WorkerUnfinishedReasonReminderError extends Error {
|
|
|
50
50
|
this.name = "WorkerUnfinishedReasonReminderError";
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Host constraint missed a routing discriminator (#1055).
|
|
55
|
+
* Names live only in the submission schema. This notice carries the field and the received value.
|
|
56
|
+
*/
|
|
57
|
+
export function unreadableDiscriminatorNotice(field, received) {
|
|
58
|
+
if (received === undefined)
|
|
59
|
+
return `读不出 ${field}`;
|
|
60
|
+
return `读不出 ${field}:${readableGateItem(received)}`;
|
|
61
|
+
}
|
|
62
|
+
/** The received discriminator only — never the rest of the receipt. */
|
|
63
|
+
export function receivedDiscriminator(receipt, field) {
|
|
64
|
+
if (receipt === null || typeof receipt !== "object" || Array.isArray(receipt))
|
|
65
|
+
return undefined;
|
|
66
|
+
if (!Object.hasOwn(receipt, field))
|
|
67
|
+
return undefined;
|
|
68
|
+
return receipt[field];
|
|
69
|
+
}
|
|
53
70
|
/**
|
|
54
71
|
* Parent seat status unreadable for queueing (#753).
|
|
55
72
|
* Correctable back to the parent itself — not a gate officer bounce, no officer field.
|
|
@@ -6,7 +6,8 @@ import { runIdFromRunDirectory } from "./run-terminal-artifacts.js";
|
|
|
6
6
|
import { readSitianRecords, resolveSitianRecordPathInLedger, sitianReport } from "./sitian-facade.js";
|
|
7
7
|
import { findRunDirectoryById } from "./public-cli/run-lifecycle.js";
|
|
8
8
|
import { isCorrectableExecuteError } from "./submission-correctable-error.js";
|
|
9
|
-
import { failOnInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.js";
|
|
9
|
+
import { failOnInfrastructureFailureDeclaration, infrastructureFailureDiagnostic } from "./package-contracts/terminating-infrastructure.js";
|
|
10
|
+
import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.js";
|
|
10
11
|
/**
|
|
11
12
|
* Admitted run identity for the ledger subject.
|
|
12
13
|
* Prefer AK_ROLE_RUN_DIR via sole runDirectory→runId parser (public-CLI correlation);
|
|
@@ -480,11 +481,22 @@ export function createSubmissionLedgerHost(host, outputTools, failInfrastructure
|
|
|
480
481
|
});
|
|
481
482
|
let result;
|
|
482
483
|
try {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
484
|
+
// A review escalate that carries the failure declaration is the
|
|
485
|
+
// seat's own submission. Every other declaration stays on the
|
|
486
|
+
// host-failure seam. Tool identity comes from the registry map.
|
|
487
|
+
const reviewEscalate = tool.name === REVIEW_SUBMISSION_OUTPUT_TOOL_NAME
|
|
488
|
+
&& infrastructureFailureDiagnostic(params) !== undefined
|
|
489
|
+
&& params !== null
|
|
490
|
+
&& typeof params === "object"
|
|
491
|
+
&& !Array.isArray(params)
|
|
492
|
+
&& params.status === "escalate";
|
|
493
|
+
if (!reviewEscalate) {
|
|
494
|
+
failOnInfrastructureFailureDeclaration(params, {
|
|
495
|
+
failInfrastructure(error, ctx) {
|
|
496
|
+
failInfrastructure(error, ctx);
|
|
497
|
+
},
|
|
498
|
+
}, context, toolCallId, tool.bounceInfrastructureDeclaration);
|
|
499
|
+
}
|
|
488
500
|
result = await tool.execute(toolCallId, params, signal, update, context);
|
|
489
501
|
}
|
|
490
502
|
catch (error) {
|
package/package.json
CHANGED
package/souls/countersign.md
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
对于修复票,判断根因是什么、证据在哪;对于新功能设计票,判断设计是不是合理,是不是陛下想要的方向。以及因果链本身站不站得住。
|
|
23
23
|
此步不成立即封驳,不必再走后三步。
|
|
24
24
|
|
|
25
|
-
**三、相抵**
|
|
25
|
+
**三、相抵** 票面,相关票与在飞分支与法源有无抵触。票面自相抵同为相抵。
|
|
26
26
|
|
|
27
27
|
**四、缺失** 法源要求的,票面有没有漏:适用法只查两类:票面与某条 ADR 决定相抵的,和把某条决定留给别的票承接的,票面须写明该条与「由 #x 承接」;这两类之外的 ADR 没引不算缺失,不得为此封驳;验收面须覆盖票面每条主张;票面里每一条行为都须答得出《送庭前三问》第一问——由哪条接缝、哪条 run 担它:起、停、续、终、接各归谁没写的行为,即缺失;前置产物、决定、答复须在;承重主张的源头不存在、不支持、已过时,同为缺失。缺失列表在能力范围内一次扫完,尽量避免因缺失导致多轮往返。
|
|
28
28
|
|
|
29
|
-
**五、擅加**
|
|
29
|
+
**五、擅加** 票面有没有超出法源的机制、约束、验收、术语。票面按类各定一套行为或文案的,就是在要分类机制;陛下没点名要的,上呈,附单一做法。
|
|
30
30
|
|
|
31
31
|
各步取证按《审法·取证授权》:工具与方式不受限,读码、开卷、传召、下探针皆可;不得删除、覆写或移动 worktree 与临时目录之外的既存路径(《审法·取证边界》)。交付时候清理回原样
|
|
32
32
|
|
package/souls/ticket-law.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
* 陛下要什么——先用一两句写出他真正要的结果;后附撑这个读法的原话,逐字,带 uuid;源卷没有 id 的,给起居录里的来源标识。他的质问——他在问这张票、这条约束凭什么存在——要用事实或法源答到他问的那个点上,不是答字面;答不到的票不送庭,上呈。没有原话的票,写它出自哪张父票(授权在父票的对话里,给 uuid),或现状违反了哪条法源。
|
|
6
6
|
* 现象——现在看得见的错是什么;一个能复现、能开卷核的实例就够。新造的东西没有现象,写要造什么。
|
|
7
|
-
*
|
|
7
|
+
* 目的——改完以后看得见的行为是什么。一句;不按现象实例逐个定行为或文案,实例只作验收样本。
|
|
8
8
|
* 怎么验——从真实入口走一遍,看见什么算对;覆盖票面每条主张。
|
|
9
9
|
* 适用法——`docs/adr/...md` 路径,一行一条,不必复述其决定。只有本票与它某条决定相抵、或把某条决定留给别的票承接时,才在那一行写明是哪一条。
|
|
10
10
|
|
|
@@ -4,7 +4,8 @@ import { auditorRunDirectory } from "./auditor-dossier-tool.ts";
|
|
|
4
4
|
import type { HostContext } from "./host-contracts.ts";
|
|
5
5
|
import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
|
|
6
6
|
import type { PublicSummonResult } from "./public-role-summons.ts";
|
|
7
|
-
import {
|
|
7
|
+
import { officerConclusionReask } from "./gatekeeper-role.ts";
|
|
8
|
+
import { receivedDiscriminator } from "./submission-errors.ts";
|
|
8
9
|
import { coalesceSubmissionRows } from "./public-cli/terminal.ts";
|
|
9
10
|
import { readableGateItem } from "./readable-gate-item.ts";
|
|
10
11
|
|
|
@@ -226,7 +227,7 @@ export async function runComplianceAudit(options: RunComplianceAuditOptions): Pr
|
|
|
226
227
|
const summoned = await summon(subject, runDirectory, options.signal, reask, submission);
|
|
227
228
|
const decision = await projectAuditorTerminal(summoned);
|
|
228
229
|
if (decision.status === "received") {
|
|
229
|
-
reask =
|
|
230
|
+
reask = officerConclusionReask(receivedDiscriminator(decision.reply, "status"));
|
|
230
231
|
continue;
|
|
231
232
|
}
|
|
232
233
|
return decision;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* is defaulted, rewritten, or dropped (ADR 0055).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.ts";
|
|
8
|
+
import { REVIEW_QUEUE_STATUSES, REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.ts";
|
|
9
9
|
|
|
10
10
|
export const COUNTERSIGN_OUTPUT_TOOL_NAME = REVIEW_SUBMISSION_OUTPUT_TOOL_NAME;
|
|
11
11
|
|
|
@@ -37,7 +37,7 @@ export function validateRecordedCountersignOutput(verdict: unknown): Countersign
|
|
|
37
37
|
if (typeof status !== "string") {
|
|
38
38
|
throw new Error("Countersign verdict has no execution discriminator");
|
|
39
39
|
}
|
|
40
|
-
if (
|
|
40
|
+
if (REVIEW_QUEUE_STATUSES.has(status)) {
|
|
41
41
|
return verdict as CountersignVerdict;
|
|
42
42
|
}
|
|
43
43
|
throw new Error("Countersign verdict has no execution discriminator");
|
package/src/countersign-role.ts
CHANGED
|
@@ -11,13 +11,9 @@ export type { CountersignVerdict };
|
|
|
11
11
|
// #836 r16 class 1: fix/note/decisionGate are LLM/human-read narrative content —
|
|
12
12
|
// no code branches on their length or nested presence. `status` alone is the
|
|
13
13
|
// machine discriminator the queue reads.
|
|
14
|
-
// #
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// (Type.Unknown) like every other gate-queue status field in this package
|
|
18
|
-
// (notary/auditor/gatekeeper/navigator); src/role-runtime.ts:997-1025 still
|
|
19
|
-
// classifies the recorded value and reasks the countersign itself when it
|
|
20
|
-
// isn't one of the three states — code, not the transport, does that work.
|
|
14
|
+
// #1055: the three-state field is an enum+required host generation constraint
|
|
15
|
+
// on reviewSubmissionSchema. Package execute still does not reject; an unreadable
|
|
16
|
+
// value reasks the countersign itself with the received value.
|
|
21
17
|
/** 给事中票庭审读五问的交卷形状(ADR 0074);形状指引,非 schema 闸。 */
|
|
22
18
|
export const countersignVerdictSchema = reviewSubmissionSchema;
|
|
23
19
|
export type CountersignVerdictParameters = import("./review-submission.ts").ReviewSubmission;
|
package/src/gatekeeper-role.ts
CHANGED
|
@@ -4,9 +4,9 @@ import type { HostContext } from "./host-contracts.ts";
|
|
|
4
4
|
import { auditorRunDirectory } from "./auditor-dossier-tool.ts";
|
|
5
5
|
import { packagedGateStageLabel } from "./packaged-role-registry.ts";
|
|
6
6
|
import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
|
|
7
|
-
import { GatekeeperDecisionError } from "./submission-errors.ts";
|
|
7
|
+
import { GatekeeperDecisionError, receivedDiscriminator, unreadableDiscriminatorNotice } from "./submission-errors.ts";
|
|
8
8
|
import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.ts";
|
|
9
|
-
import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.ts";
|
|
9
|
+
import { REVIEW_QUEUE_STATUSES, REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.ts";
|
|
10
10
|
import {
|
|
11
11
|
GATEKEEPER_OUTPUT_TOOL_NAME,
|
|
12
12
|
gatekeeperDecisionSchema,
|
|
@@ -66,6 +66,8 @@ export type GatekeeperResult =
|
|
|
66
66
|
readonly status: "needs_reask";
|
|
67
67
|
readonly officer: GateOfficer;
|
|
68
68
|
readonly receipt: unknown;
|
|
69
|
+
/** Discriminator the queue already read. Not written back onto the receipt. */
|
|
70
|
+
readonly receivedStatus?: unknown;
|
|
69
71
|
readonly runId?: string;
|
|
70
72
|
}
|
|
71
73
|
| { readonly status: "no_receipt"; readonly stage: GateOfficer; readonly reason: string; readonly facts: NoReceiptLifecycleFacts }
|
|
@@ -210,12 +212,9 @@ function readRecord(value: unknown): Record<string, unknown> | undefined {
|
|
|
210
212
|
* `fallbackStatus` is the terminal outcome.status when the receipt body has no status key
|
|
211
213
|
* (keeps missing-args sentinel intact as the receipt).
|
|
212
214
|
*/
|
|
213
|
-
/** Map the shared review verdict onto the gate queue words (#1028). */
|
|
215
|
+
/** Map the shared review verdict onto the gate queue words (#1028 / #1055). */
|
|
214
216
|
function reviewQueueStatus(status: string): string | undefined {
|
|
215
|
-
|
|
216
|
-
if (status === "continue") return "continue";
|
|
217
|
-
if (status === "escalate") return "escalate";
|
|
218
|
-
return undefined;
|
|
217
|
+
return REVIEW_QUEUE_STATUSES.has(status) ? status : undefined;
|
|
219
218
|
}
|
|
220
219
|
|
|
221
220
|
function projectOfficerDecision(
|
|
@@ -235,7 +234,7 @@ function projectOfficerDecision(
|
|
|
235
234
|
if (queueStatus === "continue" || queueStatus === "escalate") {
|
|
236
235
|
return { status: queueStatus, officer, receipt };
|
|
237
236
|
}
|
|
238
|
-
return { status: "needs_reask", officer, receipt };
|
|
237
|
+
return { status: "needs_reask", officer, receipt, receivedStatus: status };
|
|
239
238
|
}
|
|
240
239
|
|
|
241
240
|
/**
|
|
@@ -371,12 +370,14 @@ function projectOfficerTerminal(
|
|
|
371
370
|
summoned,
|
|
372
371
|
);
|
|
373
372
|
}
|
|
373
|
+
const receipt = thisCourt.length > 0 ? thisCourt[thisCourt.length - 1] : retainedReceipt(outcome);
|
|
374
374
|
return withOfficerRunId(
|
|
375
375
|
{
|
|
376
376
|
status: "needs_reask",
|
|
377
377
|
officer,
|
|
378
378
|
// This-court receipt only (#879).
|
|
379
|
-
receipt
|
|
379
|
+
receipt,
|
|
380
|
+
receivedStatus: receivedDiscriminator(receipt, "status"),
|
|
380
381
|
},
|
|
381
382
|
summoned,
|
|
382
383
|
);
|
|
@@ -391,16 +392,11 @@ export type GatekeeperProjection = {
|
|
|
391
392
|
};
|
|
392
393
|
|
|
393
394
|
/**
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
* Every review officer uses the same `status` field and three queue words (#1028).
|
|
395
|
+
* Officer conclusion was missing or not a queue word (#1055).
|
|
396
|
+
* `received` is the status value only, not the rest of the receipt.
|
|
397
397
|
*/
|
|
398
|
-
export
|
|
399
|
-
"
|
|
400
|
-
|
|
401
|
-
/** Pick the re-ask line for the officer under review. */
|
|
402
|
-
export function officerConclusionReask(officer: GateOfficer): string {
|
|
403
|
-
return OFFICER_CONCLUSION_REASK;
|
|
398
|
+
export function officerConclusionReask(received: unknown): string {
|
|
399
|
+
return unreadableDiscriminatorNotice("status", received);
|
|
404
400
|
}
|
|
405
401
|
|
|
406
402
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Dual path: gate-dispatched and independently callable (#568 / ADR 0074).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.ts";
|
|
7
|
+
import { REVIEW_QUEUE_STATUSES, REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.ts";
|
|
8
8
|
|
|
9
9
|
export const INSPECTOR_OUTPUT_TOOL_NAME: string = REVIEW_SUBMISSION_OUTPUT_TOOL_NAME;
|
|
10
10
|
export const INSPECTOR_SOURCE_RUN_FLAG = {
|
|
@@ -30,7 +30,10 @@ export function validateRecordedInspectorOutput(value: unknown): InspectorOutput
|
|
|
30
30
|
} catch {
|
|
31
31
|
throw new Error("Inspector output has no execution discriminator");
|
|
32
32
|
}
|
|
33
|
-
if (
|
|
33
|
+
if (typeof status !== "string") {
|
|
34
|
+
throw new Error("Inspector output has no execution discriminator");
|
|
35
|
+
}
|
|
36
|
+
if (REVIEW_QUEUE_STATUSES.has(status)) {
|
|
34
37
|
return value as InspectorOutput;
|
|
35
38
|
}
|
|
36
39
|
throw new Error("Inspector output has no execution discriminator");
|
package/src/judge-role.ts
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import type { RoleHost, HostContext, HostToolResult, HostGatekeeperActions } from "./host-contracts.ts";
|
|
2
2
|
import type { Static } from "typebox";
|
|
3
|
-
import { reviewSubmissionSchema } from "./review-submission.ts";
|
|
4
|
-
import { GatekeeperDecisionError, ParentQueueReaskError } from "./submission-errors.ts";
|
|
3
|
+
import { REVIEW_QUEUE_STATUSES, reviewSubmissionSchema } from "./review-submission.ts";
|
|
4
|
+
import { GatekeeperDecisionError, ParentQueueReaskError, unreadableDiscriminatorNotice } from "./submission-errors.ts";
|
|
5
5
|
import { projectGatekeeperEscalation } from "./audit-escalation.ts";
|
|
6
6
|
|
|
7
7
|
import { readableGateItem } from "./readable-gate-item.ts";
|
|
8
8
|
import {
|
|
9
9
|
JUDGE_OUTPUT_TOOL_NAME,
|
|
10
|
-
validateAcceptedJudgeDetails,
|
|
11
10
|
type JudgeVerdict,
|
|
12
11
|
} from "./package-contracts/judge-output.ts";
|
|
13
12
|
|
|
14
|
-
const JUDGE_QUEUE_STATUSES = new Set(["converged", "continue", "escalate"]);
|
|
15
|
-
const JUDGE_STATUS_REASK =
|
|
16
|
-
"status 不是 converged、continue、escalate 三态之一。请重新交卷,status 写明其一。" as const;
|
|
17
13
|
|
|
18
14
|
export { JUDGE_OUTPUT_TOOL_NAME };
|
|
19
15
|
export type { JudgeVerdict };
|
|
@@ -25,8 +21,6 @@ export type { JudgeVerdict };
|
|
|
25
21
|
// The shared open schema preserves role-specific prose without adding another contract.
|
|
26
22
|
export const judgeVerdictSchema = reviewSubmissionSchema;
|
|
27
23
|
|
|
28
|
-
type JudgeVerdictParameters = Static<typeof judgeVerdictSchema>;
|
|
29
|
-
|
|
30
24
|
export type JudgeRoleDependencies = {
|
|
31
25
|
loadSoul(): Promise<string>;
|
|
32
26
|
};
|
|
@@ -36,10 +30,6 @@ export type JudgeRoleHostActions = HostGatekeeperActions & {
|
|
|
36
30
|
bindPriorGatePass(toolCallId: string, receipt: unknown): void;
|
|
37
31
|
};
|
|
38
32
|
|
|
39
|
-
export function validateVerdict(verdict: JudgeVerdictParameters): JudgeVerdict {
|
|
40
|
-
return validateAcceptedJudgeDetails(verdict);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
33
|
|
|
44
34
|
export function createJudgeRoleRuntime(
|
|
45
35
|
pi: RoleHost,
|
|
@@ -70,19 +60,21 @@ export function createJudgeRoleRuntime(
|
|
|
70
60
|
&& typeof (parameters as Record<string, unknown>).status === "string"
|
|
71
61
|
? (parameters as Record<string, unknown>).status as string
|
|
72
62
|
: undefined;
|
|
73
|
-
if (rawStatus === undefined || !
|
|
74
|
-
|
|
63
|
+
if (rawStatus === undefined || !REVIEW_QUEUE_STATUSES.has(rawStatus)) {
|
|
64
|
+
const received = parameters !== null && typeof parameters === "object" && !Array.isArray(parameters)
|
|
65
|
+
? (parameters as Record<string, unknown>).status
|
|
66
|
+
: undefined;
|
|
67
|
+
throw new ParentQueueReaskError(unreadableDiscriminatorNotice("status", received));
|
|
75
68
|
}
|
|
76
69
|
if (rawStatus === "escalate") {
|
|
77
70
|
// Parent escalate → throw to caller as-is; officers do not attend (#753 / #756).
|
|
78
|
-
const verdict = validateVerdict(parameters);
|
|
79
71
|
return {
|
|
80
72
|
content: [],
|
|
81
|
-
details:
|
|
73
|
+
details: parameters,
|
|
82
74
|
terminate: true as const,
|
|
83
75
|
};
|
|
84
76
|
}
|
|
85
|
-
|
|
77
|
+
// Queue membership above is the only three-state check. The receipt is not re-judged.
|
|
86
78
|
// Candidate verdict is already on the parent session books as this
|
|
87
79
|
// tool-call leaf (first-record-then-audit; run 019fea05 L61/L62).
|
|
88
80
|
// #753: 符宝郎内闸 — continue returns the raw receipt as a nonterminal tool result.
|
|
@@ -101,7 +93,7 @@ export function createJudgeRoleRuntime(
|
|
|
101
93
|
if (draftPass?.status === "continue") {
|
|
102
94
|
return {
|
|
103
95
|
content: [{ type: "text" as const, text: readableGateItem(draftPass.receipt) }],
|
|
104
|
-
details:
|
|
96
|
+
details: parameters,
|
|
105
97
|
terminate: false,
|
|
106
98
|
};
|
|
107
99
|
}
|
|
@@ -121,18 +113,18 @@ export function createJudgeRoleRuntime(
|
|
|
121
113
|
if (compliancePass?.status === "continue") {
|
|
122
114
|
return {
|
|
123
115
|
content: [draftPass, compliancePass].filter((pass) => pass !== undefined).map((pass) => ({ type: "text" as const, text: readableGateItem(pass.receipt) })),
|
|
124
|
-
details:
|
|
116
|
+
details: parameters,
|
|
125
117
|
terminate: false,
|
|
126
118
|
};
|
|
127
119
|
}
|
|
128
120
|
return {
|
|
129
121
|
content: [draftPass, compliancePass].filter((pass) => pass !== undefined).map((pass) => ({ type: "text" as const, text: readableGateItem(pass.receipt) })),
|
|
130
|
-
details:
|
|
122
|
+
details: parameters,
|
|
131
123
|
terminate: true as const,
|
|
132
124
|
};
|
|
133
125
|
} catch (error) {
|
|
134
126
|
if (error instanceof GatekeeperDecisionError && error.result.status === "escalate") {
|
|
135
|
-
return projectGatekeeperEscalation(error.result,
|
|
127
|
+
return projectGatekeeperEscalation(error.result, parameters);
|
|
136
128
|
}
|
|
137
129
|
throw error;
|
|
138
130
|
}
|
package/src/open-tool-schema.ts
CHANGED
|
@@ -12,7 +12,7 @@ function declarationIdentity(schema: TSchema): string {
|
|
|
12
12
|
return JSON.stringify(semantic);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
/** Collapse transport variants into one provider-compatible
|
|
15
|
+
/** Collapse transport variants into one provider-compatible open object. Non-three-state tools stay zero-required. */
|
|
16
16
|
export function openToolObjectFromUnion(schema: UnionSchema): TSchema {
|
|
17
17
|
const declarations = new Map<string, TSchema[]>();
|
|
18
18
|
for (const variant of schema.anyOf) {
|
|
@@ -35,10 +35,15 @@ export function openToolObjectFromUnion(schema: UnionSchema): TSchema {
|
|
|
35
35
|
return object;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
/** Open a pre-existing object transport schema
|
|
39
|
-
export function openToolObject(
|
|
38
|
+
/** Open a pre-existing object transport schema. Every field stays optional. */
|
|
39
|
+
export function openToolObject(
|
|
40
|
+
schema: TSchema & { properties: Record<string, TSchema> },
|
|
41
|
+
): TSchema {
|
|
40
42
|
const object = Type.Object(
|
|
41
|
-
Object.fromEntries(Object.entries(schema.properties).map(([name, declaration]) => [
|
|
43
|
+
Object.fromEntries(Object.entries(schema.properties).map(([name, declaration]) => [
|
|
44
|
+
name,
|
|
45
|
+
Type.Optional(described(name, declaration)),
|
|
46
|
+
])),
|
|
42
47
|
{ additionalProperties: true },
|
|
43
48
|
);
|
|
44
49
|
(object as unknown as { required: string[] }).required = [];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Package-owned Judge output leaf — no role registration surface. */
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "../review-submission.ts";
|
|
4
|
+
import { REVIEW_QUEUE_STATUSES, REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "../review-submission.ts";
|
|
5
5
|
|
|
6
6
|
export const JUDGE_OUTPUT_TOOL_NAME: string = REVIEW_SUBMISSION_OUTPUT_TOOL_NAME;
|
|
7
7
|
|
|
@@ -39,9 +39,7 @@ export function validateAcceptedJudgeDetails(verdict: unknown): JudgeVerdict {
|
|
|
39
39
|
if (typeof status !== "string") {
|
|
40
40
|
throw new Error("Judge verdict has no execution discriminator");
|
|
41
41
|
}
|
|
42
|
-
if (
|
|
43
|
-
["converged", "continue", "escalate"].includes(status)
|
|
44
|
-
) {
|
|
42
|
+
if (REVIEW_QUEUE_STATUSES.has(status)) {
|
|
45
43
|
return verdict as JudgeVerdict;
|
|
46
44
|
}
|
|
47
45
|
throw new Error("Judge verdict has no execution discriminator");
|
|
@@ -50,8 +50,9 @@ const infrastructureFailureDeclarationSchema = Type.Object(
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Compose declarations shared by terminating output tools: infrastructure failure
|
|
53
|
-
* and the optional role-asserted ticket identity. Returns an open object (additionalProperties: true
|
|
54
|
-
*
|
|
53
|
+
* and the optional role-asserted ticket identity. Returns an open object (additionalProperties: true)
|
|
54
|
+
* with the base schema's properties plus the shared declaration. Incoming required
|
|
55
|
+
* keys that still exist are kept; every other key stays optional.
|
|
55
56
|
* Static typing is preserved on the base (`as S`), so existing
|
|
56
57
|
* `Static<typeof ...>` derived parameter types are unchanged.
|
|
57
58
|
*/
|
|
@@ -78,7 +79,11 @@ export function withTerminatingOutputDeclarations<
|
|
|
78
79
|
],
|
|
79
80
|
};
|
|
80
81
|
const object = Type.Object(properties, { additionalProperties: true });
|
|
81
|
-
|
|
82
|
+
const incoming = (schema as { required?: unknown }).required;
|
|
83
|
+
const preserved = Array.isArray(incoming)
|
|
84
|
+
? incoming.filter((key): key is string => typeof key === "string" && Object.hasOwn(properties, key))
|
|
85
|
+
: [];
|
|
86
|
+
(object as unknown as { required: string[] }).required = preserved;
|
|
82
87
|
return object as unknown as S;
|
|
83
88
|
}
|
|
84
89
|
|
|
@@ -115,7 +120,7 @@ function isInfrastructureFailureDeclaration(
|
|
|
115
120
|
}
|
|
116
121
|
|
|
117
122
|
/** Non-empty trimmed diagnostic from the declaration, else undefined. */
|
|
118
|
-
function infrastructureFailureDiagnostic(
|
|
123
|
+
export function infrastructureFailureDiagnostic(
|
|
119
124
|
parameters: unknown,
|
|
120
125
|
): string | undefined {
|
|
121
126
|
if (!isInfrastructureFailureDeclaration(parameters)) return undefined;
|
package/src/review-submission.ts
CHANGED
|
@@ -1,18 +1,33 @@
|
|
|
1
|
-
/** One shared terminating receipt contract for review officers (#1028). */
|
|
1
|
+
/** One shared terminating receipt contract for review officers (#1028 / #1055). */
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
|
|
4
|
-
import { openToolObject } from "./open-tool-schema.ts";
|
|
5
4
|
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
6
5
|
|
|
7
6
|
export const REVIEW_SUBMISSION_OUTPUT_TOOL_NAME = "ak_submission_output" as const;
|
|
8
7
|
|
|
9
|
-
/**
|
|
8
|
+
/** Single schema copy of the review queue words. Runtime membership reads this set. */
|
|
9
|
+
export const REVIEW_QUEUE_WORDS = ["converged", "continue", "escalate"] as const;
|
|
10
|
+
export type ReviewQueueWord = (typeof REVIEW_QUEUE_WORDS)[number];
|
|
11
|
+
export const REVIEW_QUEUE_STATUSES: ReadonlySet<string> = new Set(REVIEW_QUEUE_WORDS);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* One object root. Codex structured output rejects a root anyOf, and status
|
|
15
|
+
* stays a required three-state enum with no omitted-status failure branch.
|
|
16
|
+
* A review seat that can still submit records a real external-dependency
|
|
17
|
+
* failure as escalate plus the existing infrastructureFailure field.
|
|
18
|
+
*/
|
|
10
19
|
export const reviewSubmissionSchema = withTerminatingOutputDeclarations(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
Type.Object({
|
|
21
|
+
status: Type.Union(
|
|
22
|
+
[
|
|
23
|
+
Type.Literal(REVIEW_QUEUE_WORDS[0]),
|
|
24
|
+
Type.Literal(REVIEW_QUEUE_WORDS[1]),
|
|
25
|
+
Type.Literal(REVIEW_QUEUE_WORDS[2]),
|
|
26
|
+
],
|
|
27
|
+
{
|
|
28
|
+
description: "审核队列三态枚举,必填。审核席仍能交卷而完成审核所需的外部依赖发生真实基础设施失败时,status 取 escalate,细节写入 infrastructureFailure。不得省略 status,不得用另两个枚举值充当放行或封驳。包侧不因格式拒收。",
|
|
29
|
+
},
|
|
30
|
+
),
|
|
16
31
|
findings: Type.Optional(Type.Unknown({ description: "审核发现,原样留存" })),
|
|
17
32
|
reason: Type.Optional(Type.Unknown({ description: "上呈理由" })),
|
|
18
33
|
violations: Type.Optional(Type.Unknown({ description: "违规条目" })),
|
|
@@ -22,8 +37,7 @@ export const reviewSubmissionSchema = withTerminatingOutputDeclarations(
|
|
|
22
37
|
note: Type.Optional(Type.Unknown({ description: "裁决附注" })),
|
|
23
38
|
evidence: Type.Optional(Type.Unknown({ description: "留存证据" })),
|
|
24
39
|
decisionGate: Type.Optional(Type.Unknown({ description: "需人权威处置的问题与选项;可说明 question、options,原样留存" })),
|
|
25
|
-
}),
|
|
26
|
-
),
|
|
40
|
+
}, { additionalProperties: true }),
|
|
27
41
|
);
|
|
28
42
|
|
|
29
43
|
export type ReviewSubmission = { readonly status?: unknown; readonly [key: string]: unknown };
|
package/src/role-runtime.ts
CHANGED
|
@@ -314,7 +314,8 @@ export {
|
|
|
314
314
|
gateOfficerForSubject,
|
|
315
315
|
};
|
|
316
316
|
export type { GatekeeperResult, GatekeeperSubject, SubmissionGateNonPassResult, GateOfficer, RunGatekeeperOptions } from "./gatekeeper-role.ts";
|
|
317
|
-
import { ParentQueueReaskError } from "./submission-errors.ts";
|
|
317
|
+
import { ParentQueueReaskError, unreadableDiscriminatorNotice } from "./submission-errors.ts";
|
|
318
|
+
import { REVIEW_QUEUE_STATUSES } from "./review-submission.ts";
|
|
318
319
|
|
|
319
320
|
export {
|
|
320
321
|
DOCTOR_EVIDENCE_TOOL_NAME,
|
|
@@ -872,8 +873,6 @@ function readDiaristRunCoordinates(ctx: HostContext): {
|
|
|
872
873
|
const DIARIST_BOUNDS_REASK =
|
|
873
874
|
"边界无法使用。请重交 sessions:每卷 path + ranges,每端以原生 id 或本轮行号二选一指名。" as const;
|
|
874
875
|
const DIARIST_ROUTING_STATUSES = new Set(["completed", "escalate"]);
|
|
875
|
-
const DIARIST_STATUS_REASK =
|
|
876
|
-
"status 不是 completed、escalate 之一。请重新交卷,status 写明其一。" as const;
|
|
877
876
|
|
|
878
877
|
/**
|
|
879
878
|
* #708 / #779 / #901: 起居郎 public seat on the shared filed-officer envelope.
|
|
@@ -900,7 +899,7 @@ export function createDiaristRoleRuntime(
|
|
|
900
899
|
// before interpreting any other submitted field; caller-owned data stays open.
|
|
901
900
|
const status = submitted?.status;
|
|
902
901
|
if (typeof status !== "string" || !DIARIST_ROUTING_STATUSES.has(status)) {
|
|
903
|
-
throw new ParentQueueReaskError(
|
|
902
|
+
throw new ParentQueueReaskError(unreadableDiscriminatorNotice("status", submitted?.status));
|
|
904
903
|
}
|
|
905
904
|
if (status === "escalate") return parameters;
|
|
906
905
|
const assertion = readDiaristTicketAssertion(submitted);
|
|
@@ -943,26 +942,9 @@ export function createDiaristRoleRuntime(
|
|
|
943
942
|
);
|
|
944
943
|
}
|
|
945
944
|
|
|
946
|
-
/**
|
|
947
|
-
const COUNTERSIGN_QUEUE_STATUSES = new Set(["converged", "continue", "escalate"]);
|
|
948
|
-
|
|
949
|
-
/**
|
|
950
|
-
* Plain-language re-ask when status is not a known queue word.
|
|
951
|
-
* Back to countersign itself — notary is not summoned (#753).
|
|
952
|
-
*/
|
|
953
|
-
const COUNTERSIGN_STATUS_REASK =
|
|
954
|
-
"status 不是 converged、continue、escalate 三态之一。请重新交卷,status 写明其一。" as const;
|
|
955
|
-
|
|
956
|
-
/** Secretariat status words the submission gate reads. */
|
|
945
|
+
/** Secretariat status words the submission gate reads. Not the review three-state field. */
|
|
957
946
|
const SECRETARIAT_QUEUE_STATUSES = new Set(["converged", "escalate"]);
|
|
958
947
|
|
|
959
|
-
/**
|
|
960
|
-
* Plain-language re-ask when secretariatStatus is not a known queue word.
|
|
961
|
-
* Back to secretariat itself — 给事中 is not summoned for escalation.
|
|
962
|
-
*/
|
|
963
|
-
const SECRETARIAT_STATUS_REASK =
|
|
964
|
-
"secretariatStatus 不是 converged、escalate 之一。请重新交卷,status 写明其一。" as const;
|
|
965
|
-
|
|
966
948
|
/**
|
|
967
949
|
* Secretariat output is terminal; converged submissions enter the shared
|
|
968
950
|
* submission gate on every host that mounts gatekeeper actions.
|
|
@@ -989,7 +971,9 @@ export function createSecretariatRoleRuntime(
|
|
|
989
971
|
? record.secretariatStatus
|
|
990
972
|
: undefined;
|
|
991
973
|
if (status === undefined || !SECRETARIAT_QUEUE_STATUSES.has(status)) {
|
|
992
|
-
throw new ParentQueueReaskError(
|
|
974
|
+
throw new ParentQueueReaskError(
|
|
975
|
+
unreadableDiscriminatorNotice("secretariatStatus", record?.secretariatStatus),
|
|
976
|
+
);
|
|
993
977
|
}
|
|
994
978
|
if (status === "escalate") {
|
|
995
979
|
// Parent escalate → throw to caller as-is; countersign does not attend.
|
|
@@ -1132,10 +1116,10 @@ export function createCountersignRoleRuntime(
|
|
|
1132
1116
|
record !== undefined && typeof record.status === "string"
|
|
1133
1117
|
? record.status
|
|
1134
1118
|
: undefined;
|
|
1135
|
-
if (status === undefined || !
|
|
1119
|
+
if (status === undefined || !REVIEW_QUEUE_STATUSES.has(status)) {
|
|
1136
1120
|
// Parent status unreadable → back to countersign itself; do not summon notary,
|
|
1137
1121
|
// do not forge an officer bounce face (#753).
|
|
1138
|
-
throw new ParentQueueReaskError(
|
|
1122
|
+
throw new ParentQueueReaskError(unreadableDiscriminatorNotice("status", record?.status));
|
|
1139
1123
|
}
|
|
1140
1124
|
if (status === "escalate") {
|
|
1141
1125
|
// Parent escalate → throw to caller as-is; notary does not attend (#753).
|
package/src/submission-errors.ts
CHANGED
|
@@ -58,6 +58,22 @@ export class WorkerUnfinishedReasonReminderError extends Error {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Host constraint missed a routing discriminator (#1055).
|
|
63
|
+
* Names live only in the submission schema. This notice carries the field and the received value.
|
|
64
|
+
*/
|
|
65
|
+
export function unreadableDiscriminatorNotice(field: string, received: unknown): string {
|
|
66
|
+
if (received === undefined) return `读不出 ${field}`;
|
|
67
|
+
return `读不出 ${field}:${readableGateItem(received)}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The received discriminator only — never the rest of the receipt. */
|
|
71
|
+
export function receivedDiscriminator(receipt: unknown, field: string): unknown {
|
|
72
|
+
if (receipt === null || typeof receipt !== "object" || Array.isArray(receipt)) return undefined;
|
|
73
|
+
if (!Object.hasOwn(receipt, field)) return undefined;
|
|
74
|
+
return (receipt as Record<string, unknown>)[field];
|
|
75
|
+
}
|
|
76
|
+
|
|
61
77
|
/**
|
|
62
78
|
* Parent seat status unreadable for queueing (#753).
|
|
63
79
|
* Correctable back to the parent itself — not a gate officer bounce, no officer field.
|