@akagilnc/pi-workflow-roles 0.1.5357 → 0.1.5365

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.5357",
3
+ "version": "0.1.5365",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -18,38 +18,12 @@ export { validateRecordedDiaristOutput };
18
18
  /**
19
19
  * 起居郎交卷形状。
20
20
  * #901:交边界(sessions);正文与不可解析原字节由机械投影。
21
+ * status 的合法词(completed | escalate)写在 description,结算认这些词。
22
+ * sessions / ticketSessions 是投影所读的边界:卷路径或端点读不出时走既有 reask,不中止本轮。
23
+ * 声明只留字段名和语义说明,不留类型、嵌套、长度、必填。
21
24
  */
22
- const diaristSessionsSchema = Type.Array(
23
- Type.Object(
24
- {
25
- path: Type.Optional(Type.String({ description: "会话卷绝对或可读路径" })),
26
- ranges: Type.Optional(
27
- Type.Array(
28
- Type.Object(
29
- {
30
- from: Type.Optional(
31
- Type.Object(
32
- { id: Type.Optional(Type.String()), line: Type.Optional(Type.Unknown()) },
33
- { additionalProperties: true, description: "起点:原生 id 或本轮行号二选一" },
34
- ),
35
- ),
36
- to: Type.Optional(
37
- Type.Object(
38
- { id: Type.Optional(Type.String()), line: Type.Optional(Type.Unknown()) },
39
- { additionalProperties: true, description: "终点:原生 id 或本轮行号二选一" },
40
- ),
41
- ),
42
- },
43
- { additionalProperties: true, description: "一段对话区间" },
44
- ),
45
- { description: "本卷本轮各区间;至少一段" },
46
- ),
47
- ),
48
- },
49
- { additionalProperties: true, description: "一卷会话的边界" },
50
- ),
51
- { description: "对话边界;空列表=本轮无对话可划。端点无法指名时走 reask,不中止。" },
52
- );
25
+ const DIARIST_SESSIONS_DESCRIPTION =
26
+ "对话边界。每卷含 path(会话卷绝对或可读路径)与 ranges(本卷本轮各区间)。每段区间含 from / to;起点与终点各写 id(原生 id)或 line(本轮行号),二选一。空列表=本轮无对话可划。端点无法指名时走 reask,不中止。" as const;
53
27
 
54
28
  export const diaristOutputSchema = withTerminatingOutputDeclarations(
55
29
  openToolObject(
@@ -63,24 +37,13 @@ export const diaristOutputSchema = withTerminatingOutputDeclarations(
63
37
  "本庭对象票号(正整数)或 null/省略=真无票;下游走 typed 键;认不出用 status=escalate,不洗成无录。机械层不重判。",
64
38
  }),
65
39
  ),
66
- reason: Type.Optional(
67
- Type.String({
68
- description: "status 为 escalate 时:认不出本庭对象的原因",
69
- }),
70
- ),
71
- sessions: Type.Optional(diaristSessionsSchema),
72
- ticketSessions: Type.Optional(
73
- Type.Array(
74
- Type.Object(
75
- {
76
- ticketNumber: Type.Unknown({ description: "本条边界所归票号(正整数)" }),
77
- sessions: diaristSessionsSchema,
78
- },
79
- { additionalProperties: true },
80
- ),
81
- { description: "多票庭逐票对话边界;一次交卷各票各自分录。与单票 sessions 二选一。" },
82
- ),
83
- ),
40
+ reason: Type.Unknown({
41
+ description: "status 为 escalate 时:认不出本庭对象的原因",
42
+ }),
43
+ sessions: Type.Optional(Type.Unknown({ description: DIARIST_SESSIONS_DESCRIPTION })),
44
+ ticketSessions: Type.Unknown({
45
+ description: `多票庭逐票对话边界;每票含 ticketNumber(本条边界所归票号,正整数)与 sessions(${DIARIST_SESSIONS_DESCRIPTION})。一次交卷各票各自分录。与单票 sessions 二选一。`,
46
+ }),
84
47
  }),
85
48
  ),
86
49
  );
@@ -1,6 +1,6 @@
1
1
  import { Type } from "typebox";
2
2
  import { canonicalJson } from "./canonical-json.ts";
3
- import { openToolObjectFromUnion } from "./open-tool-schema.ts";
3
+ import { openToolObject } from "./open-tool-schema.ts";
4
4
  import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
5
5
 
6
6
  export const DOCTOR_EVIDENCE_TOOL_NAME = "ak_doctor_evidence";
@@ -9,6 +9,11 @@ export const DOCTOR_AUDIT_TOOL_NAME = "ak_doctor_audit_decision";
9
9
  export const DOCTOR_OUTPUT_TOOL_DESCRIPTION = "提交唯一终局单案证词;completed 允许空 findings。";
10
10
  export const DOCTOR_TARGET_KINDS = ["law", "gate", "template", "station", "seat"] as const;
11
11
  export type DoctorTargetKind = typeof DOCTOR_TARGET_KINDS[number];
12
+ const DOCTOR_DISPOSITIONS = ["keep", "thin", "delete"] as const;
13
+ const DOCTOR_PRESCRIPTION_KINDS = ["retain", "delete", "simplify", "patch", "addMechanism"] as const;
14
+ const biteActual = "actual" as const;
15
+ const biteNone = "noRealBite" as const;
16
+ const DOCTOR_BITE_KINDS = [biteActual, biteNone] as const;
12
17
  export type DoctorCaseIdentity = { issueNumber: number; runsPath: string };
13
18
  export type DoctorSessionCost =
14
19
  | { source: string; startedAt: string; endedAt: string; wallMilliseconds: number; completion: "accepted" }
@@ -24,12 +29,12 @@ export type DoctorCaseCost = {
24
29
  };
25
30
  export type DoctorGuardrailAnswer = { answer: boolean; evidenceIds: string[]; explanation: string };
26
31
  export type DoctorLastRealBite =
27
- | { kind: "actual"; targetKey: string; evidenceId: string }
28
- | { kind: "noRealBite"; targetKey: string; eligibleEvidenceIds: string[] };
32
+ | { kind: typeof biteActual; targetKey: string; evidenceId: string }
33
+ | { kind: typeof biteNone; targetKey: string; eligibleEvidenceIds: string[] };
29
34
  type DoctorFindingBody = {
30
- evidenceIds: string[]; disposition: "keep" | "thin" | "delete";
35
+ evidenceIds: string[]; disposition: (typeof DOCTOR_DISPOSITIONS)[number];
31
36
  guardrails: { reproducibleFailure: DoctorGuardrailAnswer; owningSeamOrInvariant: DoctorGuardrailAnswer; deletionOrSimplificationSuffices: DoctorGuardrailAnswer };
32
- prescription: { kind: "retain" | "delete" | "simplify" | "patch" | "addMechanism"; recommendation: string; necessityExplanation?: string };
37
+ prescription: { kind: (typeof DOCTOR_PRESCRIPTION_KINDS)[number]; recommendation: string; necessityExplanation?: string };
33
38
  lastRealBite: DoctorLastRealBite;
34
39
  };
35
40
  type DoctorAssetKind = DoctorTargetKind;
@@ -45,55 +50,39 @@ export type DoctorOutput = DoctorSubmission;
45
50
  export type DoctorEvidenceEntry = { id: string; kind: "session" | "stderr"; byteLength: number; contentLength: number; sha256: string; content: string };
46
51
  export type DoctorCase = { version: 1; identity: DoctorCaseIdentity; evidence: DoctorEvidenceEntry[]; cost: DoctorCaseCost };
47
52
 
48
- // #836 r16 class 1: case/finding/assetEvidence/guardrails/prescription/
49
- // lastRealBite/missingEvidence are LLM/human-read narrative content — Judge/
50
- // 台院 read the original volume, no code branches on their length or nested
51
- // presence (src/doctor-contracts.ts:136-138 passes the submission through
52
- // unprojected). Field/type/description/Literal value stay; provider
53
- // required/minLength/minItems/minimum/nested additionalProperties:false is deleted.
54
- const nonblank = Type.Optional(Type.String());
55
- const evidenceIds = Type.Optional(Type.Array(Type.String()));
56
- const guardrail = Type.Object({ answer: Type.Optional(Type.Boolean()), evidenceIds, explanation: nonblank }, { additionalProperties: true });
57
- const lastRealBite = Type.Union([
58
- Type.Object({ kind: Type.Optional(Type.Literal("actual")), targetKey: nonblank, evidenceId: nonblank }, { additionalProperties: true }),
59
- Type.Object({ kind: Type.Optional(Type.Literal("noRealBite")), targetKey: nonblank, eligibleEvidenceIds: evidenceIds }, { additionalProperties: true }),
60
- ]);
61
- const assetKinds = DOCTOR_TARGET_KINDS;
62
- const findingBody = {
63
- evidenceIds, disposition: Type.Optional(Type.Union([Type.Literal("keep"), Type.Literal("thin"), Type.Literal("delete")])),
64
- guardrails: Type.Optional(Type.Object({ reproducibleFailure: guardrail, owningSeamOrInvariant: guardrail, deletionOrSimplificationSuffices: guardrail }, { additionalProperties: true })),
65
- prescription: Type.Optional(Type.Object({ kind: Type.Optional(Type.Union([Type.Literal("retain"), Type.Literal("delete"), Type.Literal("simplify"), Type.Literal("patch"), Type.Literal("addMechanism")])), recommendation: nonblank, necessityExplanation: nonblank }, { additionalProperties: true })), lastRealBite: Type.Optional(lastRealBite),
66
- };
67
- const finding = Type.Union([
68
- Type.Object({ targetKey: nonblank, observation: nonblank, evidenceIds }, { additionalProperties: true }),
69
- Type.Object({ targetKey: nonblank, targetKind: Type.Optional(Type.Union(assetKinds.map((kind) => Type.Literal(kind)))), assetEvidence: Type.Optional(Type.Object({ targetKey: nonblank, targetKind: Type.Optional(Type.Union(assetKinds.map((kind) => Type.Literal(kind)))), evidenceId: nonblank }, { additionalProperties: true })), ...findingBody }, { additionalProperties: true }),
70
- ]);
71
- const caseIdentity = Type.Object({ issueNumber: Type.Optional(Type.Integer()), runsPath: nonblank }, { additionalProperties: true });
72
- // #836 (ADR 0003 Amendment): status kept open like countersignStatus
73
- // (src/countersign-role.ts) — one shared description across both variants
74
- // so openToolObjectFromUnion's identical-declaration collapse drops none of it.
53
+ // status 的合法词写在 description。交卷原样入账(validateDoctorSubmissionShape),
54
+ // 本文件不按 completed | refused 改道。case / findings / reason / missingEvidence
55
+ // 同样不按长度或嵌套改道,台院读原卷。声明只留字段名和语义说明,不留类型、嵌套、
56
+ // 枚举、长度、必填。
57
+ // The evidence-read action tool below keeps its own constraints — code reads
58
+ // evidenceId/offset/limit to look up and slice (DoctorEvidenceStore.read), which
59
+ // is a live-target binding, not a submission shape gate (ADR 0037).
75
60
  const DOCTOR_STATUS_DESCRIPTION =
76
61
  "completed | refused。completed 允许空 findings;refused 仅当证据不足以支撑如实案证词" as const;
77
- const doctorSubmissionVariants = Type.Union([
78
- Type.Object({
79
- status: Type.Unknown({ description: DOCTOR_STATUS_DESCRIPTION }),
80
- case: Type.Unsafe({ ...caseIdentity, description: "留存太医署案身份" }),
81
- findings: Type.Array(finding, { description: "可空或仅含非处方案观察;缺可复用资产或 bounded-bite 证据只排除对应资产处方" }),
82
- }, { additionalProperties: false, description: "单案证词,不要求任何处方或可复用 finding" }),
83
- Type.Object({
84
- status: Type.Unknown({ description: DOCTOR_STATUS_DESCRIPTION }),
85
- reason: Type.String({ description: "证据不足以支撑如实证词的原因" }),
86
- missingEvidence: Type.Array(Type.Object({ need: nonblank, targetKeys: evidenceIds }, { additionalProperties: true }), { description: "如实证词所需而尚缺的证据" }),
87
- }, { additionalProperties: false, description: "证据不足以支撑如实案证词" }),
88
- ]);
62
+ const doctorSubmissionObject = Type.Object({
63
+ status: Type.Unknown({ description: DOCTOR_STATUS_DESCRIPTION }),
64
+ case: Type.Unknown({
65
+ description:
66
+ "留存太医署案身份,原样留存(如 issueNumber、runsPath)。机械层另按绑定 run 投影案身份,不在本字段重判。",
67
+ }),
68
+ findings: Type.Unknown({
69
+ description:
70
+ `逐条资产观察与处方,原样留存。一条可含 targetKey、targetKind(${DOCTOR_TARGET_KINDS.join(" | ")})、observation、evidenceIds、disposition(${DOCTOR_DISPOSITIONS.join(" | ")})、assetEvidence(targetKey、targetKind、evidenceId)、guardrails(reproducibleFailure、owningSeamOrInvariant、deletionOrSimplificationSuffices,各项可含 answer、evidenceIds、explanation)、prescription(kind 为 ${DOCTOR_PRESCRIPTION_KINDS.join(" | ")},另可写 recommendation、necessityExplanation)、lastRealBite(kind 为 ${DOCTOR_BITE_KINDS.join(" | ")},另可写 targetKey、evidenceId、eligibleEvidenceIds)。不要求任何处方或可复用 finding;缺可复用资产或 bounded-bite 证据只排除对应资产处方。机器不核验。`,
71
+ }),
72
+ reason: Type.Unknown({
73
+ description: "证据不足以支撑如实证词的原因;仅 refused 时用。",
74
+ }),
75
+ missingEvidence: Type.Unknown({
76
+ description: "如实证词所需而尚缺的证据,原样留存(如每项 need、targetKeys)。",
77
+ }),
78
+ });
89
79
  export const doctorSubmissionSchema = withTerminatingOutputDeclarations(
90
- openToolObjectFromUnion(doctorSubmissionVariants),
80
+ openToolObject(doctorSubmissionObject),
91
81
  );
92
82
  // #836 r16 class 3: action tool — code reads evidenceId to look up the Map entry
93
- // and offset/limit to slice + accumulate coverage (src/doctor-contracts.ts:97-128,
94
- // src/doctor-role.ts:38); those constraints stay. Root additionalProperties:false
95
- // is deleted — no reader consumes extra fields, so a closed object only rejects
96
- // the role for saying more (src/doctor-role.ts execute reads named params only).
83
+ // and offset/limit to slice + accumulate coverage (DoctorEvidenceStore.read);
84
+ // those constraints stay. Root additionalProperties:false is deleted — no reader
85
+ // consumes extra fields, so a closed object only rejects the role for saying more.
97
86
  export const doctorEvidenceReadSchema = Type.Object({ evidenceId: Type.String({ minLength: 1, description: "待读留存证据标识" }), offset: Type.Optional(Type.Integer({ minimum: 0, description: "起始字节偏移(从 0 计)" })), limit: Type.Optional(Type.Integer({ minimum: 1, description: "返回字节数(无上限)" })) }, { additionalProperties: true });
98
87
  export class DoctorSubmissionContractError extends Error { override readonly name = "DoctorSubmissionContractError"; }
99
88
  function isRecord(value: unknown): value is Record<string, unknown> { return typeof value === "object" && value !== null && !Array.isArray(value); }
@@ -17,23 +17,22 @@ export { validateRecordedGleanerLeftOutput };
17
17
 
18
18
  // #836 r16 class 1: pointer/statement are LLM/human-read narrative content — no
19
19
  // code branches on their presence.
20
- /** 左拾遗弹章交卷形状。 */
20
+ // #1134: `status` alone is the trajectory field (src/gleaner-left-contracts.ts:38
21
+ // recognizes completed to settle); its word rides the description. findings is
22
+ // narrative content declared name + semantic description only — the array type
23
+ // and the nested pointer/statement object shape are deleted, because the package
24
+ // declaration IS the host's pre-dispatch validator. An empty findings list stays
25
+ // a lawful completion; an omitted one is now expressible too.
21
26
  export const gleanerLeftOutputSchema = withTerminatingOutputDeclarations(
22
27
  openToolObject(
23
28
  Type.Object({
24
29
  status: Type.Unknown({
25
30
  description: "completed",
26
31
  }),
27
- findings: Type.Array(
28
- Type.Object(
29
- {
30
- pointer: Type.Optional(Type.String({ description: "文件/行指针" })),
31
- statement: Type.Optional(Type.String({ description: "疑点陈述" })),
32
- },
33
- { additionalProperties: true, description: "一条弹章" },
34
- ),
35
- { description: "弹章列表;空列表合法完局" },
36
- ),
32
+ findings: Type.Unknown({
33
+ description:
34
+ "弹章列表;每条可含 pointer(文件/行指针)与 statement(疑点陈述),原样留存。空列表合法完局;机器不判弹章是否成立。",
35
+ }),
37
36
  }),
38
37
  ),
39
38
  );
@@ -1,6 +1,6 @@
1
1
  import { Type, type Static } from "typebox";
2
2
  import { exactUtf8 } from "./exact-utf8.ts";
3
- import { openToolObjectFromUnion } from "./open-tool-schema.ts";
3
+ import { openToolObject } from "./open-tool-schema.ts";
4
4
  import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
5
5
 
6
6
  const materialSchema = Type.Object({ bytesBase64: Type.String(), sha256: Type.String() }, { additionalProperties: false });
@@ -14,18 +14,25 @@ export const mergerInputSchema = Type.Object({
14
14
  resolutionScope: Type.Array(Type.String(), { description: "材料:解析范围提示;可空" }),
15
15
  authorizedChecks: Type.Array(checkSchema),
16
16
  }, { additionalProperties: false });
17
- // #836 r16 class 1: attemptId/report/diagnosis are LLM/human-read narrative
18
- // content (candidate stored as submitted) — no code branches on their length.
19
- // mergerInputSchema (host-authored material, not role output) is out of scope.
20
- // #836 (ADR 0003 Amendment): status kept open like countersignStatus
21
- // (src/countersign-role.ts) — one shared description across both variants
22
- // so openToolObjectFromUnion's identical-declaration collapse drops none of it.
17
+ // #836 r16 class 1: attemptId/report/diagnosis/mergeCommitId are LLM/human-read
18
+ // narrative content (candidate stored as submitted) — no code branches on their
19
+ // length. mergerInputSchema (host-authored material, not role output) is out of scope.
20
+ // #1134: `status` alone is the trajectory field (src/packaged-role-registry.ts:327-328
21
+ // receiptCommitWhen picks the commit read off a completed receipt); its words ride
22
+ // the description. Every other output field is declared name + semantic description
23
+ // only — string type, required and the two-variant union are deleted, because the
24
+ // package declaration IS the host's pre-dispatch validator.
23
25
  const MERGER_STATUS_DESCRIPTION = "completed | escalate" as const;
24
- const mergerOutputVariants = Type.Union([
25
- Type.Object({ status: Type.Unknown({ description: MERGER_STATUS_DESCRIPTION }), attemptId: Type.String({ description: "已受理合并 attempt 身份" }), report: Type.String({ description: "如实结果报告" }), mergeCommitId: Type.String({ description: "完成合并 commit object ID" }) }, { additionalProperties: false }),
26
- Type.Object({ status: Type.Unknown({ description: MERGER_STATUS_DESCRIPTION }), attemptId: Type.String({ description: "已受理合并 attempt 身份" }), diagnosis: Type.String({ description: "合并无法或不应由本席完成的原因(含无进行中合并、无活可干、需新的产品/权力决定)" }), report: Type.String({ description: "如实结果报告" }) }, { additionalProperties: false }),
27
- ]);
28
- export const mergerOutputSchema = withTerminatingOutputDeclarations(openToolObjectFromUnion(mergerOutputVariants));
26
+ const mergerOutputObject = Type.Object({
27
+ status: Type.Unknown({ description: MERGER_STATUS_DESCRIPTION }),
28
+ attemptId: Type.Unknown({ description: "已受理合并 attempt 身份" }),
29
+ report: Type.Unknown({ description: "如实结果报告" }),
30
+ mergeCommitId: Type.Unknown({ description: "完成合并 commit object ID" }),
31
+ diagnosis: Type.Unknown({
32
+ description: "合并无法或不应由本席完成的原因(含无进行中合并、无活可干、需新的产品/权力决定)",
33
+ }),
34
+ });
35
+ export const mergerOutputSchema = withTerminatingOutputDeclarations(openToolObject(mergerOutputObject));
29
36
 
30
37
  export type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T extends readonly (infer U)[] ? readonly DeepReadonly<U>[] : T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> } : T;
31
38
  export type MergerMaterial = DeepReadonly<Static<typeof materialSchema>>;
@@ -1,41 +1,17 @@
1
1
  import { Type, type TSchema } from "typebox";
2
2
 
3
- type UnionSchema = TSchema & { anyOf: Array<TSchema & { properties?: Record<string, TSchema> }> };
4
-
5
3
  function described(name: string, schema: TSchema): TSchema {
6
4
  if (typeof (schema as { description?: unknown }).description === "string") return schema;
7
5
  throw new Error(`Tool field ${name} has no semantic description at its schema owner`);
8
6
  }
9
7
 
10
- function declarationIdentity(schema: TSchema): string {
11
- const { description: _description, ...semantic } = schema as TSchema & { description?: string };
12
- return JSON.stringify(semantic);
13
- }
14
-
15
- /** Collapse transport variants into one provider-compatible open object. Non-three-state tools stay zero-required. */
16
- export function openToolObjectFromUnion(schema: UnionSchema): TSchema {
17
- const declarations = new Map<string, TSchema[]>();
18
- for (const variant of schema.anyOf) {
19
- for (const [name, declaration] of Object.entries(variant.properties ?? {})) {
20
- const entries = declarations.get(name) ?? [];
21
- const identity = declarationIdentity(declaration);
22
- if (!entries.some((entry) => declarationIdentity(entry) === identity)) entries.push(declaration);
23
- declarations.set(name, entries);
24
- }
25
- }
26
- const properties = Object.fromEntries([...declarations].map(([name, entries]) => {
27
- const descriptions = [...new Set(entries.map((entry) => (entry as { description?: unknown }).description).filter((value): value is string => typeof value === "string"))].join(" ");
28
- const declaration = entries.length === 1
29
- ? entries[0]!
30
- : Type.Union(entries, descriptions === "" ? {} : { description: descriptions });
31
- return [name, Type.Optional(described(name, declaration))];
32
- }));
33
- const object = Type.Object(properties, { additionalProperties: true });
34
- (object as unknown as { required: string[] }).required = [];
35
- return object;
36
- }
37
-
38
- /** Open a pre-existing object transport schema. Every field stays optional. */
8
+ /**
9
+ * One open tool object per submission tool. Every field stays optional, the root
10
+ * accepts extra keys, and each field declaration carries a semantic description
11
+ * with no type/nesting/enum/length constraint (#1134): the package declaration
12
+ * is what the host validates before dispatch, so anything narrower than a name
13
+ * plus its meaning is a rejection rule no owner approved.
14
+ */
39
15
  export function openToolObject(
40
16
  schema: TSchema & { properties: Record<string, TSchema> },
41
17
  ): TSchema {
@@ -48,4 +24,4 @@ export function openToolObject(
48
24
  );
49
25
  (object as unknown as { required: string[] }).required = [];
50
26
  return object;
51
- }
27
+ }
@@ -1,68 +1,65 @@
1
- import { Type, type Static } from "typebox";
2
- import { openToolObjectFromUnion } from "../open-tool-schema.ts";
1
+ import { Type } from "typebox";
2
+ import { openToolObject } from "../open-tool-schema.ts";
3
3
  import { withTerminatingOutputDeclarations } from "./terminating-infrastructure.ts";
4
4
 
5
5
  export const FIXER_OUTPUT_TOOL_NAME = "ak_fixer_output";
6
6
 
7
- // #836 r16 class 1: these are LLM/human-read narrative content — the candidate
8
- // row lands on the ledger as submitted (src/submission-ledger.ts:351-390) and no
9
- // code branches on its shape. Field/type/description/Literal value stay; provider
10
- // required/minLength/minItems is deleted. `reason` alone keeps minLength: the
11
- // worker gate reads `reason.trim().length > 0` to pick typed-reminder-bounce vs
12
- // accept (src/worker-submission-gates.ts:159-163,297-302).
13
- const transportString = Type.String();
14
- const authorityBlockerSchema = Type.Object({ cause: Type.Optional(Type.Literal("authority_violation")), evidence: Type.Optional(transportString) });
15
- const prerequisiteBlockerSchema = Type.Object({ cause: Type.Optional(Type.Literal("prerequisite_unmet")), prerequisiteId: Type.Optional(transportString), evidence: Type.Optional(transportString) });
16
- const blockerSchema = Type.Union([authorityBlockerSchema, prerequisiteBlockerSchema]);
17
- const exceptionSchema = Type.Object({ where: Type.Optional(transportString), reason: Type.Optional(transportString) });
18
- /** ⑥ test evidence slip — require submit when diff has test changes; machine does not check existence/completeness/coverage. */
19
- const testEvidenceSchema = Type.Object({
20
- contract: Type.Optional(Type.String({ description: "测试改动所证明的契约" })),
21
- minimumNecessaryCost: Type.Optional(Type.String({ description: "测试改动的一行最小必要成本" })),
22
- measuredDuration: Type.Optional(Type.String({ description: "聚焦验证实测时长" })),
23
- }, { description: "测试证据条;diff 含测试改动时提交;机器不核验。" });
24
- const completedClassResultSchema = Type.Object({
25
- name: Type.Optional(transportString),
26
- disposition: Type.Optional(Type.Literal("completed")),
27
- searchScope: Type.Optional(transportString),
28
- exceptions: Type.Optional(Type.Array(exceptionSchema)),
29
- commitSha: Type.Optional(transportString),
30
- });
31
- const refusedClassResultSchema = Type.Object({
32
- name: Type.Optional(transportString),
33
- disposition: Type.Optional(Type.Literal("refused")),
34
- remainingScope: Type.Optional(transportString),
35
- blocker: Type.Optional(blockerSchema),
36
- });
37
- const classResultSchema = Type.Union([completedClassResultSchema, refusedClassResultSchema]);
38
- const completedClassResultsSchema = Type.Array(completedClassResultSchema);
39
-
40
7
  /** Owner 2026-09-28: partially_completed only when a lawful refusal exists; never otherwise (ADR 0015). */
41
8
  export const PARTIALLY_COMPLETED_DEFINITION =
42
9
  "partially_completed 只能在有合法拒绝项(完成项与合法拒绝项并存)时用,别的情况不准用。" as const;
43
- // #836 (ADR 0003 Amendment): status kept open like countersignStatus
44
- // (src/countersign-role.ts) — one shared description across every variant
45
- // so openToolObjectFromUnion's identical-declaration collapse drops none of it.
10
+ // status 的合法词写在 description。completed / partially_completed 进入提交闸;
11
+ // unfinished 且未见理由说明时,运行时同 run 催全(ADR 0050),理由在不在不按 JSON 类型判。
12
+ // 其余字段入账原样留存。声明只留字段名和语义说明,不留类型、嵌套、枚举、长度、必填。
46
13
  const FIXER_STATUS_DESCRIPTION =
47
14
  `planned | completed | refused | unfinished | partially_completed。unfinished 缺前置或违宪约束致本局未完成时可用,缺待决 owner 决定或答复属缺前置。${PARTIALLY_COMPLETED_DEFINITION}` as const;
48
- const fixerOutputVariants = Type.Union([
49
- Type.Object({ status: Type.Unknown({ description: FIXER_STATUS_DESCRIPTION }), report: Type.String({ description: "如实结果报告" }) }),
50
- Type.Object({ status: Type.Unknown({ description: FIXER_STATUS_DESCRIPTION }), report: Type.String({ description: "如实结果报告" }), remainingScope: Type.String({ description: "依法不能完成的工作范围" }), blocker: Type.Unsafe({ ...blockerSchema, description: "合法阻断完成的 blocker" }) }),
51
- Type.Object({ status: Type.Unknown({ description: FIXER_STATUS_DESCRIPTION }), report: Type.String({ description: "如实结果报告" }), remainingScope: Type.String({ description: "本局后剩余工作" }), reason: Type.Optional(Type.String({ minLength: 1, description: "阻断原因:缺前置或违宪约束。缺待决 owner 决定或答复属缺前置。" })), classResults: Type.Optional(Type.Unsafe({ ...completedClassResultsSchema, description: "本局已完成的 class 结算" })), testEvidence: Type.Optional(testEvidenceSchema) }),
52
- Type.Object({ status: Type.Unknown({ description: FIXER_STATUS_DESCRIPTION }), report: Type.String({ description: "如实结果报告" }), classResults: Type.Array(classResultSchema, { description: "已完成的 class 结算" }), testEvidence: Type.Optional(testEvidenceSchema) }),
53
- Type.Object({ status: Type.Unknown({ description: FIXER_STATUS_DESCRIPTION }), report: Type.String({ description: "如实结果报告" }), classResults: Type.Array(classResultSchema, { description: "各类拒绝结算" }) }),
54
- Type.Object({ status: Type.Unknown({ description: FIXER_STATUS_DESCRIPTION }), report: Type.String({ description: "如实结果报告" }), classResults: Type.Array(classResultSchema, { description: "各类完成或拒绝结算" }), testEvidence: Type.Optional(testEvidenceSchema) }),
55
- ]);
15
+
16
+ const fixerOutputObject = Type.Object({
17
+ status: Type.Unknown({ description: FIXER_STATUS_DESCRIPTION }),
18
+ report: Type.Unknown({ description: "如实结果报告" }),
19
+ remainingScope: Type.Unknown({
20
+ description: "本局后剩余工作;依法不能完成的范围也写此处。",
21
+ }),
22
+ blocker: Type.Unknown({
23
+ description:
24
+ "合法阻断完成的 blocker,原样留存。可写 cause(authority_violation 或 prerequisite_unmet)、evidence;prerequisite_unmet 还可写 prerequisiteId。",
25
+ }),
26
+ reason: Type.Unknown({
27
+ description: "阻断原因:缺前置或违宪约束。缺待决 owner 决定或答复属缺前置。",
28
+ }),
29
+ classResults: Type.Unknown({
30
+ description:
31
+ "各类 class 结算,原样留存。完成项的 disposition 为 completed,可写 name、searchScope、exceptions(where、reason)、commitSha。拒绝项的 disposition 为 refused,可写 name、remainingScope、blocker。",
32
+ }),
33
+ testEvidence: Type.Unknown({
34
+ description:
35
+ "测试证据条;diff 含测试改动时提交;机器不核验。可写 contract(测试改动所证明的契约)、minimumNecessaryCost(测试改动的一行最小必要成本)、measuredDuration(聚焦验证实测时长)。",
36
+ }),
37
+ });
38
+
56
39
  export const fixerOutputSchema = withTerminatingOutputDeclarations(
57
- openToolObjectFromUnion(fixerOutputVariants),
40
+ openToolObject(fixerOutputObject),
58
41
  );
59
42
 
60
- export type FixerBlocker = Static<typeof blockerSchema>;
61
- export type FixerClassResult = Static<typeof classResultSchema>;
62
- export type FixerTestEvidence = Static<typeof testEvidenceSchema>;
63
- export type FixerOutput = Static<typeof fixerOutputVariants>;
43
+ export type FixerBlocker = Readonly<Record<string, unknown>>;
44
+ export type FixerClassResult = Readonly<Record<string, unknown>>;
45
+ export type FixerTestEvidence = Readonly<Record<string, unknown>>;
46
+ /**
47
+ * One shared open receipt shape (#1134): every field is optional and carries no
48
+ * type constraint, matching what `openToolObject` hands the host. The union of
49
+ * per-status variants is gone — the declaration no longer discriminates, and the
50
+ * trajectory word is read off `status` at runtime, not off the schema.
51
+ */
52
+ export type FixerOutput = {
53
+ readonly status?: unknown;
54
+ readonly report?: unknown;
55
+ readonly remainingScope?: unknown;
56
+ readonly blocker?: unknown;
57
+ readonly reason?: unknown;
58
+ readonly classResults?: unknown;
59
+ readonly testEvidence?: unknown;
60
+ };
64
61
  export type FixerPhase = "plan" | "apply";
65
62
 
66
63
  export function validateFixerOutput(value: unknown, _phase?: FixerPhase): FixerOutput {
67
64
  return value as FixerOutput;
68
- }
65
+ }
@@ -1,6 +1,6 @@
1
1
  import type { RoleHost, HostContext, HostToolResult } from "./host-contracts.ts";
2
2
  import { Type } from "typebox";
3
- import { openToolObjectFromUnion } from "./open-tool-schema.ts";
3
+ import { openToolObject } from "./open-tool-schema.ts";
4
4
  import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
5
5
 
6
6
  import { REVIEWER_OUTPUT_TOOL_NAME, type ReviewerIntent } from "./package-contracts/reviewer-output.ts";
@@ -17,43 +17,24 @@ export type ReviewerAdmittedInputs = Readonly<{
17
17
  ticketNumber?: number;
18
18
  }>;
19
19
 
20
- const reviewerAmendmentsSchema = Type.Object({
21
- completeness: Type.Optional(Type.String({
22
- description:
23
- "completeness lens 完整报告:candidates、逐条处置与 verdict;非仅 verdict 一行。hard-stop/usage error 为 refused 时,若已产出报告仍照录于此",
24
- })),
25
- correctness: Type.Optional(Type.String({
26
- description:
27
- "correctness lens 完整报告:candidates、逐条处置与 verdict;非仅 verdict 一行。hard-stop/usage error 为 refused 时,若已产出报告仍照录于此",
28
- })),
29
- }, {
30
- additionalProperties: true,
31
- description:
32
- "已运行 lens 的 amendments 承载 candidates、逐条处置与 verdict 的完整报告(非仅 verdict 行);显式单轴时另一轴可省略。hard-stop/usage error 为 refused 时,已产出报告仍照录对应 lens 字段。",
33
- });
34
- // #836 r16 class 1: diagnostic is LLM/human-read narrative content — no code
35
- // branches on its length (src/reviewer-role.ts consumer: reviewer content is
36
- // returned as submitted, ADR 0057).
37
- // #836 (ADR 0003 Amendment): status kept open like countersignStatus
38
- // (src/countersign-role.ts) — one shared description across both variants
39
- // so openToolObjectFromUnion's identical-declaration collapse drops none of it.
40
- // #917 §6: both normal lens verdicts → completed; hard-stop/usage error → refused
41
- // (report already produced still lands in selected-lens amendments).
20
+ const REVIEWER_AMENDMENTS_DESCRIPTION =
21
+ "已运行 lens 的 amendments 承载 candidates、逐条处置与 verdict 的完整报告(非仅 verdict 行);显式单轴时另一轴可省略(completeness / correctness)。hard-stop/usage error 为 refused 时,已产出报告仍照录对应 lens 字段。" as const;
22
+ // #1134: `status` alone is the trajectory field (#917 §6 two normal lens verdicts →
23
+ // completed; hard-stop / usage error → refused); its words ride the description.
24
+ // amendments/diagnostic are LLM/human-read narrative content returned as
25
+ // submitted — declaration keeps name + semantic description only, no nested
26
+ // object shape, no string type, no required constraint.
42
27
  const REVIEWER_STATUS_DESCRIPTION =
43
28
  "completed | refused。两种正常 lens verdict(completeness / correctness)均 completed;hard-stop 与 usage error 为 refused(已产出报告仍照录进所选 lens amendments)。" as const;
44
- const reviewerOutputVariants = Type.Union([
45
- Type.Object({
46
- status: Type.Unknown({ description: REVIEWER_STATUS_DESCRIPTION }),
47
- amendments: Type.Optional(reviewerAmendmentsSchema),
48
- }, { additionalProperties: false }),
49
- Type.Object({
50
- status: Type.Unknown({ description: REVIEWER_STATUS_DESCRIPTION }),
51
- diagnostic: Type.String({ description: "拒绝诊断说明;hard-stop/usage error 原因进此字段,已产出报告另照录 amendments" }),
52
- amendments: Type.Optional(reviewerAmendmentsSchema),
53
- }, { additionalProperties: false }),
54
- ]);
29
+ const reviewerOutputObject = Type.Object({
30
+ status: Type.Unknown({ description: REVIEWER_STATUS_DESCRIPTION }),
31
+ amendments: Type.Unknown({ description: REVIEWER_AMENDMENTS_DESCRIPTION }),
32
+ diagnostic: Type.Unknown({
33
+ description: "拒绝诊断说明;hard-stop/usage error 原因进此字段,已产出报告另照录 amendments",
34
+ }),
35
+ });
55
36
  export const reviewerOutputSchema = withTerminatingOutputDeclarations(
56
- openToolObjectFromUnion(reviewerOutputVariants),
37
+ openToolObject(reviewerOutputObject),
57
38
  );
58
39
  export type ReviewerRoleDependencies = {
59
40
  loadSoul(): Promise<string>;
@@ -12,7 +12,17 @@ import { SECRETARIAT_OUTPUT_TOOL_NAME, type SecretariatVerdict } from "./secreta
12
12
  export { SECRETARIAT_OUTPUT_TOOL_NAME } from "./secretariat-contracts.ts";
13
13
  export type { SecretariatVerdict };
14
14
 
15
- /** 中书省终局回执形状。 */
15
+ /**
16
+ * 中书省终局回执形状。
17
+ * #1134: `secretariatStatus` alone is the trajectory field
18
+ * (src/packaged-role-registry.ts:406 receiptStatusKey; the public settlement reads
19
+ * converged | escalate to route); its words ride the description. note/evidence/
20
+ * decisionGate are declared name + semantic description only — the string type, the
21
+ * number type, the nested question/options object shape and the required constraint
22
+ * are deleted, because the package declaration IS the host's pre-dispatch validator.
23
+ * (ticketNumber keeps its own declaration: it is the declared seat identity the
24
+ * activation flag and settlement read, not narrative content.)
25
+ */
16
26
  export const secretariatVerdictSchema = withTerminatingOutputDeclarations(
17
27
  Type.Object(
18
28
  {
@@ -20,19 +30,13 @@ export const secretariatVerdictSchema = withTerminatingOutputDeclarations(
20
30
  description: "converged | escalate",
21
31
  }),
22
32
  ticketNumber: Type.Optional(
23
- Type.Number({ description: "本票号;署时指向最终正文所在票" }),
33
+ Type.Unknown({ description: "本票号;署时指向最终正文所在票" }),
24
34
  ),
25
- note: Type.Optional(Type.String({ description: "附注" })),
35
+ note: Type.Unknown({ description: "附注" }),
26
36
  evidence: Type.Optional(Type.Unknown({ description: "留存证据" })),
27
- decisionGate: Type.Optional(
28
- Type.Object(
29
- {
30
- question: Type.Optional(Type.String()),
31
- options: Type.Optional(Type.Array(Type.String())),
32
- },
33
- { additionalProperties: true, description: "需陛下处置的问题与选项" },
34
- ),
35
- ),
37
+ decisionGate: Type.Unknown({
38
+ description: "需陛下处置的问题与选项;可写 question 与 options,原样留存。",
39
+ }),
36
40
  },
37
41
  { additionalProperties: true },
38
42
  ),