@akagilnc/pi-workflow-roles 0.1.5357 → 0.1.5368
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 +5353 -5480
- package/dist/doctor-contracts.js +22 -34
- package/dist/headless-host/production-host.js +1215 -1342
- package/dist/merger-contracts.js +11 -6
- package/dist/migrate-book-topology.js +57 -101
- package/dist/open-tool-schema.js +1 -25
- package/dist/package-contracts/fixer-output.js +21 -35
- package/dist/public-cli/auto-resume.js +1 -1
- package/dist/public-cli/cli.js +57 -56
- package/dist/public-cli/config.js +4 -0
- package/dist/public-cli/instruction-seat-run.js +65 -40
- package/dist/public-cli/invocation.js +132 -128
- package/dist/public-cli/main.js +449 -506
- package/dist/public-cli/post-admission.js +85 -86
- package/dist/public-cli/role-seat-runtime.js +29 -0
- package/dist/public-cli/run-lifecycle.js +6 -6
- package/dist/public-cli/turn-request.js +14 -0
- package/dist/public-role-summons.js +20 -33
- package/dist/role-run-placement.js +4 -0
- package/package.json +1 -1
- package/src/book-topology-migration-placement.ts +3 -5
- package/src/diarist-role.ts +12 -49
- package/src/doctor-contracts.ts +38 -49
- package/src/gleaner-left-role.ts +10 -11
- package/src/merger-contracts.ts +19 -12
- package/src/open-tool-schema.ts +8 -32
- package/src/package-contracts/fixer-output.ts +49 -52
- package/src/public-cli/auto-resume.ts +1 -1
- package/src/public-cli/cli.ts +67 -73
- package/src/public-cli/config.ts +8 -0
- package/src/public-cli/instruction-seat-run.ts +82 -55
- package/src/public-cli/invocation.ts +148 -162
- package/src/public-cli/post-admission.ts +110 -100
- package/src/public-cli/role-seat-runtime.ts +72 -0
- package/src/public-cli/run-lifecycle.ts +7 -26
- package/src/public-cli/terminal.ts +2 -17
- package/src/public-cli/turn-request.ts +26 -0
- package/src/public-role-summons.ts +20 -45
- package/src/reviewer-role.ts +16 -35
- package/src/role-run-placement.ts +5 -0
- package/src/secretariat-role.ts +16 -12
- package/src/worker-role.ts +14 -29
- package/src/worker-submission-gates.ts +14 -3
package/src/doctor-contracts.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { canonicalJson } from "./canonical-json.ts";
|
|
3
|
-
import {
|
|
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:
|
|
28
|
-
| { kind:
|
|
32
|
+
| { kind: typeof biteActual; targetKey: string; evidenceId: string }
|
|
33
|
+
| { kind: typeof biteNone; targetKey: string; eligibleEvidenceIds: string[] };
|
|
29
34
|
type DoctorFindingBody = {
|
|
30
|
-
evidenceIds: string[]; disposition:
|
|
35
|
+
evidenceIds: string[]; disposition: (typeof DOCTOR_DISPOSITIONS)[number];
|
|
31
36
|
guardrails: { reproducibleFailure: DoctorGuardrailAnswer; owningSeamOrInvariant: DoctorGuardrailAnswer; deletionOrSimplificationSuffices: DoctorGuardrailAnswer };
|
|
32
|
-
prescription: { kind:
|
|
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
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
|
|
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
|
|
78
|
-
Type.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
Type.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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 (
|
|
94
|
-
//
|
|
95
|
-
//
|
|
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); }
|
package/src/gleaner-left-role.ts
CHANGED
|
@@ -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.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
);
|
package/src/merger-contracts.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type, type Static } from "typebox";
|
|
2
2
|
import { exactUtf8 } from "./exact-utf8.ts";
|
|
3
|
-
import {
|
|
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
|
|
18
|
-
// content (candidate stored as submitted) — no code branches on their
|
|
19
|
-
// mergerInputSchema (host-authored material, not role output) is out of scope.
|
|
20
|
-
// #
|
|
21
|
-
//
|
|
22
|
-
//
|
|
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
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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>>;
|
package/src/open-tool-schema.ts
CHANGED
|
@@ -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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
2
|
-
import {
|
|
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
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
40
|
+
openToolObject(fixerOutputObject),
|
|
58
41
|
);
|
|
59
42
|
|
|
60
|
-
export type FixerBlocker =
|
|
61
|
-
export type FixerClassResult =
|
|
62
|
-
export type FixerTestEvidence =
|
|
63
|
-
|
|
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
|
+
}
|
|
@@ -210,7 +210,7 @@ function jsonSafeReplacer(): (key: string, value: unknown) => unknown {
|
|
|
210
210
|
* + O_NOFOLLOW where the platform provides it (a planted symlink is never
|
|
211
211
|
* followed) — mirrors settlement.ts's own hardened artifact writers.
|
|
212
212
|
*/
|
|
213
|
-
async function writeHardenedArtifactFile(
|
|
213
|
+
export async function writeHardenedArtifactFile(
|
|
214
214
|
artifactsDir: string,
|
|
215
215
|
namePrefix: string,
|
|
216
216
|
payload: Record<string, unknown>,
|