@akagilnc/pi-workflow-roles 0.1.5241 → 0.1.5245

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.
Files changed (39) hide show
  1. package/README.md +2 -3
  2. package/README.zh-CN.md +2 -3
  3. package/dist/acp-host/production-host.js +442 -3070
  4. package/dist/collector-config.js +0 -1
  5. package/dist/headless-host/production-host.js +443 -3071
  6. package/dist/migrate-book-topology.js +40 -107
  7. package/dist/package-contracts/collector-output.js +1 -90
  8. package/dist/package-contracts/terminating-tools.js +2 -2
  9. package/dist/packaged-role-registry.js +8 -11
  10. package/dist/public-cli/invocation.js +2 -22
  11. package/dist/public-cli/main.js +252 -698
  12. package/dist/public-cli/option-definitions.js +4 -19
  13. package/dist/public-cli/settlement.js +2 -79
  14. package/package.json +1 -1
  15. package/resources/collector-bot-handbook.md +4 -2
  16. package/src/collector-config.ts +0 -1
  17. package/src/collector-role.ts +40 -535
  18. package/src/collector-tool-schemas.ts +7 -117
  19. package/src/gh-api-runner.ts +102 -0
  20. package/src/package-contracts/collector-output.ts +15 -174
  21. package/src/package-contracts/terminating-tools.ts +0 -2
  22. package/src/packaged-role-registry.ts +7 -10
  23. package/src/public-cli/invocation.ts +3 -27
  24. package/src/public-cli/option-definitions.ts +4 -19
  25. package/src/public-cli/settlement.ts +3 -102
  26. package/src/role-runtime-dependencies.ts +0 -4
  27. package/src/role-runtime.ts +16 -98
  28. package/src/ticket-snapshot.ts +1 -1
  29. package/dist/collector-evidence.js +0 -319
  30. package/dist/collector-github.js +0 -725
  31. package/dist/collector-identity.js +0 -92
  32. package/dist/collector-ledger.js +0 -888
  33. package/dist/collector-target.js +0 -169
  34. package/src/collector-evidence.ts +0 -466
  35. package/src/collector-github.ts +0 -1032
  36. package/src/collector-handbook.ts +0 -194
  37. package/src/collector-identity.ts +0 -151
  38. package/src/collector-ledger.ts +0 -1186
  39. package/src/collector-target.ts +0 -207
@@ -2,136 +2,26 @@ import { Type, type Static } from "typebox";
2
2
  import { openToolObject } from "./open-tool-schema.ts";
3
3
  import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
4
4
 
5
- // #836 r16 class 3: execute() reads no params for this tool
6
- // (src/collector-role.ts:431-459) — a closed root only rejects the role for
7
- // saying more; no reader consumes extra fields.
8
- export const collectorObserveArgsSchema = Type.Object({}, { additionalProperties: true });
9
- // #836 r16 class 4: the regex was a real provider shape rejection ahead of the
10
- // ledger's own identity check — src/collector-ledger.ts:911-972 already trims
11
- // and enforces non-empty/lookup/marker/attemptKey identity on requestId once
12
- // the tool receives it, so the duplicate pattern-based rejection is deleted
13
- // (r15b decision, not reopened here).
14
- export const collectorRequestArgsSchema = Type.Object({
15
- requestId: Type.String({
16
- minLength: 1,
17
- description: "请求身份(配置 id 或角色判定的稳定 id;首尾无空白)",
18
- }),
19
- snapshotId: Type.String({ minLength: 1, description: "最新留存观察快照" }),
20
- // #836 r16 class 3: body.minLength deleted — manifest-configured requestId
21
- // ignores body, and the unknown-requestId non-empty condition is a real
22
- // branch in src/collector-ledger.ts:922-951, not a provider shape gate.
23
- body: Type.Optional(Type.String({
24
- description: "角色判定的请求正文;request-manifest 未收录该 requestId 时必填",
25
- })),
26
- }, { additionalProperties: true });
27
- /** UTF-8 byte ceiling per handbook body — keeps next activation materials inside context budget. */
28
- export const COLLECTOR_HANDBOOK_MAX_BYTES = 64 * 1024;
29
- export const collectorHandbookWriteArgsSchema = Type.Object({
30
- scope: Type.Union([
31
- Type.Literal("general"),
32
- Type.Literal("repo"),
33
- ], { description: "general=通用手册;repo=当前仓库差异" }),
34
- body: Type.String({
35
- description: `手册全文(opaque 工作记忆;整份替换;UTF-8 至多 ${COLLECTOR_HANDBOOK_MAX_BYTES} 字节)`,
36
- }),
37
- }, { additionalProperties: true });
38
- export const collectorReadArgsSchema = Type.Object({
39
- evidenceId: Type.String({ minLength: 1, description: "observe 返回的材料证据 id(evidenceId)" }),
40
- }, { additionalProperties: true });
41
- export const collectorWaitArgsSchema = Type.Object({
42
- durationMs: Type.Integer({
43
- minimum: 1,
44
- description: "等待毫秒;实际睡眠不超过剩余等待窗(#678;无包内单次任意上限)",
45
- }),
46
- }, { additionalProperties: true });
47
-
48
5
  /**
49
- * #678 D4: open the wait window at a work step.
50
- * Omit startedAt for existing-PR trigger-phase end (= now).
51
- * Pass PR creation success time for new-PR auto-trigger rounds.
52
- */
53
- // #836 r16 class 3: startedAt.minLength deleted — an empty string is already
54
- // handled as omission by src/collector-role.ts:586-597, so the provider
55
- // nonblank gate is not load-bearing for any branch.
56
- export const collectorOpenWaitWindowArgsSchema = Type.Object({
57
- startedAt: Type.Optional(Type.String({
58
- description: "等待窗起点 ISO 时间;新建 PR 用创建成功时刻;省略=现在(触发阶段结束)",
59
- })),
60
- }, { additionalProperties: true });
61
-
62
- /**
63
- * #676 A: role-decided target bind. The model judges task materials and submits
64
- * the chosen PR and/or issue identity; runtime only performs online association
65
- * for the role-chosen ticket — never scrapes task text to lock a target.
66
- */
67
- export const collectorBindTargetArgsSchema = Type.Object({
68
- prNumber: Type.Optional(Type.Unknown({
69
- description: "本仓 PR 号(正整数)。",
70
- })),
71
- issueNumber: Type.Optional(Type.Unknown({
72
- description: "本仓 issue 号(正整数)。",
73
- })),
74
- }, { additionalProperties: true });
75
-
76
- /**
77
- * #641 chain① / #676 C / ADR 0057: nested finding item declarations for model
78
- * guidance. Host must not pure-shape-reject the envelope (第 0 条). Runtime binds
79
- * resolvable evidence pointers only; unprojected content stays distinguishable.
80
- */
81
- const collectorFindingItemDeclaration = (() => {
82
- // Nested declarations for model guidance only — open required so host cannot
83
- // pure-shape-reject missing optional fields (第 0 条 / ADR 0057 / #676 C).
84
- const item = Type.Object(
85
- {
86
- evidenceId: Type.Unknown({
87
- description: "observe 返回的材料指针(必填语义)",
88
- }),
89
- category: Type.Unknown({
90
- description: "简短归类标签,不是摘要",
91
- }),
92
- summary: Type.Unknown({
93
- description: "哪个 bot、什么问题的摘要;不誊抄正文",
94
- }),
95
- },
96
- {
97
- additionalProperties: true,
98
- description:
99
- "单条 finding 指针:evidenceId,以及可选 category、summary。",
100
- },
101
- );
102
- (item as unknown as { required: string[] }).required = [];
103
- return item;
104
- })();
105
-
106
- /**
107
- * Field declarations + descriptions are guidance for the model — host must not
108
- * pure-shape-reject the envelope (第 0 条 / ADR 0057).
6
+ * #1088: LLM gathers evidence via host CLI and submits the receipt.
7
+ * Field declarations guide the model — host must not pure-shape-reject (第 0 条 / ADR 0057).
8
+ * Presence of `groups` remains the Collector terminal discriminator for settlement/analyst.
109
9
  */
110
10
  export const collectorOutputBaseSchema = openToolObject(
111
11
  Type.Object({
112
- // No root type:array — host must not shape-reject non-array findings (#676 C).
113
- // Nested item declarations ride `items` for registration preservation (ADR 0057).
114
- findings: Type.Unsafe({
12
+ groups: Type.Unsafe({
115
13
  description:
116
- "本次收集到的逐条 findings 指针。",
117
- items: collectorFindingItemDeclaration,
14
+ "按机器身份归组的出席、材料与逐条 findings(LLM 自行从宿主 CLI 取证整理;代码不代收不归并)。",
118
15
  }),
119
16
  unfinishedReasons: Type.Unknown({
120
- description:
121
- "未完成原因。",
17
+ description: "未完成原因;取证受限时如实报告。",
122
18
  }),
123
19
  }),
124
20
  );
125
21
 
126
- /** Runtime owns the observed evidence; the model submits findings and signals sole-final submission. */
22
+ /** Runtime does not enrich findings; the model submits the receipt and signals sole-final. */
127
23
  export const collectorOutputArgsSchema = withTerminatingOutputDeclarations(
128
24
  collectorOutputBaseSchema,
129
25
  );
130
26
 
131
- export type CollectorObserveArgs = Static<typeof collectorObserveArgsSchema>;
132
- export type CollectorRequestArgs = Static<typeof collectorRequestArgsSchema>;
133
- export type CollectorReadArgs = Static<typeof collectorReadArgsSchema>;
134
- export type CollectorWaitArgs = Static<typeof collectorWaitArgsSchema>;
135
- export type CollectorOpenWaitWindowArgs = Static<typeof collectorOpenWaitWindowArgsSchema>;
136
- export type CollectorBindTargetArgs = Static<typeof collectorBindTargetArgsSchema>;
137
27
  export type CollectorOutputArgs = Static<typeof collectorOutputArgsSchema>;
@@ -0,0 +1,102 @@
1
+ import { spawn } from "node:child_process";
2
+
3
+ export type GhApiRunOptions = {
4
+ signal?: AbortSignal;
5
+ };
6
+
7
+ export type GhApiResponse = {
8
+ status: number;
9
+ headers: Record<string, string>;
10
+ bodyText: string;
11
+ };
12
+
13
+ export type GhApiRunner = (
14
+ args: readonly string[],
15
+ options?: GhApiRunOptions,
16
+ ) => Promise<GhApiResponse>;
17
+
18
+ export function createGhApiRunner(
19
+ options: {
20
+ spawnImpl?: typeof spawn;
21
+ env?: NodeJS.ProcessEnv;
22
+ } = {},
23
+ ): GhApiRunner {
24
+ const spawnImpl = options.spawnImpl ?? spawn;
25
+ return async (args, runOptions = {}) => {
26
+ return await new Promise<GhApiResponse>((resolve, reject) => {
27
+ const signal = runOptions.signal;
28
+ if (signal?.aborted) {
29
+ reject(signal.reason ?? new Error("aborted"));
30
+ return;
31
+ }
32
+ const child = spawnImpl("gh", args, {
33
+ env: options.env ?? process.env,
34
+ stdio: ["ignore", "pipe", "pipe"],
35
+ });
36
+ let stdout = "";
37
+ let stderr = "";
38
+ let settled = false;
39
+ const settle = (fn: () => void) => {
40
+ if (settled) return;
41
+ settled = true;
42
+ if (signal !== undefined) {
43
+ signal.removeEventListener("abort", onAbort);
44
+ }
45
+ fn();
46
+ };
47
+ const onAbort = () => {
48
+ try {
49
+ child.kill("SIGTERM");
50
+ } catch (error) {
51
+ settle(() => reject(error));
52
+ return;
53
+ }
54
+ settle(() => {
55
+ reject(signal?.reason ?? new Error("aborted"));
56
+ });
57
+ };
58
+ if (signal !== undefined) {
59
+ signal.addEventListener("abort", onAbort, { once: true });
60
+ }
61
+ child.stdout.setEncoding("utf8").on("data", (chunk) => {
62
+ stdout += chunk;
63
+ });
64
+ child.stderr.setEncoding("utf8").on("data", (chunk) => {
65
+ stderr += chunk;
66
+ });
67
+ child.on("error", (error) => {
68
+ settle(() => reject(error));
69
+ });
70
+ child.on("close", (code, signal) => {
71
+ settle(() => {
72
+ // gh api --include prints: HTTP/ headers blank-line body
73
+ const match = stdout.match(/^HTTP\/[\d.]+\s+(\d+)[^\n]*\r?\n([\s\S]*?)\r?\n\r?\n([\s\S]*)$/);
74
+ if (match) {
75
+ const status = Number(match[1]);
76
+ const headerText = match[2] ?? "";
77
+ const bodyText = match[3] ?? "";
78
+ const headers: Record<string, string> = {};
79
+ for (const line of headerText.split(/\r?\n/)) {
80
+ const idx = line.indexOf(":");
81
+ if (idx === -1) continue;
82
+ const name = line.slice(0, idx).trim().toLowerCase();
83
+ const value = line.slice(idx + 1).trim();
84
+ headers[name] = value;
85
+ }
86
+ resolve({ status, headers, bodyText });
87
+ return;
88
+ }
89
+ if (code === 0) {
90
+ resolve({ status: 200, headers: {}, bodyText: stdout });
91
+ return;
92
+ }
93
+ const failure = new Error(
94
+ `gh api failed without a parseable HTTP response (code=${String(code)}): ${stderr || stdout}`,
95
+ { cause: { code, signal, stderr, stdout } },
96
+ );
97
+ reject(failure);
98
+ });
99
+ });
100
+ });
101
+ };
102
+ }
@@ -1,188 +1,29 @@
1
1
  /** Package-owned Collector receipt leaf — no role registration surface. */
2
2
 
3
3
  export const COLLECTOR_OUTPUT_TOOL = "ak_collector_output";
4
- export const COLLECTOR_HOST = "github.com";
5
4
 
5
+ /**
6
+ * One identity group on the LLM-submitted receipt.
7
+ * #1088: code no longer builds ledger projections; the model submits groups.
8
+ */
6
9
  export type CollectorIdentityGroup = {
7
10
  identity: Record<string, unknown> | null;
8
11
  displayLogin?: string;
9
- attendance: true;
10
- materials: Array<Record<string, unknown>>;
11
- findings: Array<Record<string, unknown>>;
12
- };
13
-
14
- export type CollectorRequestAttempt = {
15
- attemptId: string;
16
- requestId: string;
17
- observedHead: string;
18
- snapshotId: string;
19
- marker: string;
20
- body: string;
21
- startedAt: string;
22
- status: "started" | "succeeded" | "rejected" | "ambiguous_loss" | "recovered";
23
- responseDiagnostics?: string;
24
- commentEvidenceId?: string;
25
- recoverySnapshotId?: string;
26
- };
27
-
28
- export type CollectorSnapshot = {
29
- snapshotId: string;
30
- observedAt: string;
31
- completedAt: string;
32
- completedMono: number;
33
- host: "github.com";
34
- repository: string;
35
- prNumber: number;
36
- prState: string;
37
- headOid: string;
38
- complete: boolean;
39
- evidenceIds: string[];
40
- pageDiagnostics: Array<Record<string, unknown>>;
41
- normalizedByteLength: number;
12
+ attendance?: true;
13
+ materials?: Array<Record<string, unknown>>;
14
+ findings?: Array<Record<string, unknown>>;
42
15
  };
43
16
 
44
17
  /**
45
- * #641 chain① receipt evidence record: pointer/integrity facts only. Bodies and
46
- * raw payloads stay in the volume (observe details + GitHub pointer) and are
47
- * never transcribed into the receipt.
18
+ * Collector receipt as submitted. Presence of `groups` is the terminal
19
+ * discriminator for settlement/analyst (#836: original object is the receipt).
48
20
  */
49
- export type CollectorEvidenceRecord = {
50
- evidenceId: string;
51
- kind: string;
52
- versionId: string;
53
- contentDigest: string;
54
- firstObservedAt: string;
55
- githubId?: number;
56
- authorLogin?: string;
57
- htmlUrl?: string;
58
- authoritativeTime?: string | null;
59
- };
60
-
61
- /**
62
- * #676 C: sole structured projection-fact type (builder + package leaf).
63
- * Builder always emits the full object; package leaf keeps it optional so old
64
- * volumes without the field stay readable — missing is unknown, never defaulted.
65
- */
66
- export type CollectorSubmissionProjection = {
67
- findingsSource: "absent" | "array" | "unreadable";
68
- findingsProjectedCount: number;
69
- findingsUnprojected: boolean;
70
- unfinishedReasonsSource: "absent" | "array" | "unreadable";
71
- unfinishedReasonsProjectedCount: number;
72
- unfinishedReasonsUnprojected: boolean;
73
- };
74
-
75
21
  export type CollectorReceipt = {
76
- host: "github.com";
77
- repository: string;
78
- prNumber: number;
79
- /**
80
- * Final observed PR state; non-OPEN still delivers materials (#676 D6).
81
- * Absent/unreadable on old volumes is unknown — never defaulted to OPEN.
82
- */
22
+ groups: CollectorIdentityGroup[] | ReadonlyArray<Record<string, unknown>>;
23
+ unfinishedReasons?: unknown;
24
+ host?: string;
25
+ repository?: string;
26
+ prNumber?: number;
83
27
  prState?: string;
84
- manifestDigest: string;
85
- activationTime: string;
86
- deadlineTime: string;
87
- finalObservationTime: string;
88
- finalSnapshotId: string;
89
- targetHead: string;
90
- groups: CollectorIdentityGroup[];
91
- unfinishedReasons?: string[];
92
- /**
93
- * Distinguishes readable-empty from unprojected original submission content (#676 C).
94
- * Package leaf may carry a partial projection when some sibling keys are unreadable —
95
- * missing keys stay absent (never defaulted false/0/OPEN).
96
- */
97
- submissionProjection?: Partial<CollectorSubmissionProjection>;
98
- requestAttempts: CollectorRequestAttempt[];
99
- snapshots: CollectorSnapshot[];
100
- evidenceRecords: CollectorEvidenceRecord[];
28
+ manifestDigest?: string;
101
29
  };
102
-
103
- function safeGet(value: unknown, key: string): unknown {
104
- if ((typeof value !== "object" && typeof value !== "function") || value === null) return undefined;
105
- try { return (value as Record<string, unknown>)[key]; } catch { return undefined; }
106
- }
107
- function records(value: unknown): Record<string, unknown>[] {
108
- return Array.isArray(value) ? value.filter((item): item is Record<string, unknown> => item !== null && typeof item === "object") : [];
109
- }
110
- function strings(value: unknown): string[] {
111
- return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : [];
112
- }
113
-
114
- /** Settlement projection. Presence of the canonical groups array is the one Collector terminal discriminator. */
115
- export function validateAcceptedCollectorReceipt(value: unknown): CollectorReceipt {
116
- const rawGroups = safeGet(value, "groups");
117
- if (!Array.isArray(rawGroups)) throw new Error("Collector receipt has no typed groups terminal discriminator");
118
- const groups = records(rawGroups).map((group) => ({
119
- identity: (safeGet(group, "identity") ?? null) as Record<string, unknown> | null,
120
- ...(typeof safeGet(group, "displayLogin") === "string" ? { displayLogin: safeGet(group, "displayLogin") as string } : {}),
121
- attendance: true as const,
122
- materials: records(safeGet(group, "materials")),
123
- findings: records(safeGet(group, "findings")),
124
- }));
125
- const unfinishedRaw = safeGet(value, "unfinishedReasons");
126
- const unfinishedReasons = strings(unfinishedRaw);
127
- // #676 C: project readable submissionProjection fields independently — do not
128
- // drop the whole block when one sibling key is the wrong type (left-拾遗1).
129
- const submissionProjection = projectSubmissionProjection(safeGet(value, "submissionProjection"));
130
- const prStateRaw = safeGet(value, "prState");
131
- const prState = typeof prStateRaw === "string" ? prStateRaw : undefined;
132
- return {
133
- host: safeGet(value, "host") as "github.com",
134
- repository: safeGet(value, "repository") as string,
135
- prNumber: safeGet(value, "prNumber") as number,
136
- ...(prState === undefined ? {} : { prState }),
137
- manifestDigest: safeGet(value, "manifestDigest") as string,
138
- activationTime: safeGet(value, "activationTime") as string,
139
- deadlineTime: safeGet(value, "deadlineTime") as string,
140
- finalObservationTime: safeGet(value, "finalObservationTime") as string,
141
- finalSnapshotId: safeGet(value, "finalSnapshotId") as string,
142
- targetHead: safeGet(value, "targetHead") as string,
143
- groups,
144
- ...(unfinishedReasons.length > 0 ? { unfinishedReasons } : {}),
145
- ...(submissionProjection === undefined ? {} : { submissionProjection }),
146
- requestAttempts: records(safeGet(value, "requestAttempts")) as unknown as CollectorRequestAttempt[],
147
- snapshots: records(safeGet(value, "snapshots")).map((snapshot) => ({
148
- snapshotId: safeGet(snapshot, "snapshotId"), observedAt: safeGet(snapshot, "observedAt"), completedAt: safeGet(snapshot, "completedAt"), completedMono: safeGet(snapshot, "completedMono"), host: safeGet(snapshot, "host"), repository: safeGet(snapshot, "repository"), prNumber: safeGet(snapshot, "prNumber"), prState: safeGet(snapshot, "prState"), headOid: safeGet(snapshot, "headOid"), complete: safeGet(snapshot, "complete"), evidenceIds: strings(safeGet(snapshot, "evidenceIds")), pageDiagnostics: records(safeGet(snapshot, "pageDiagnostics")), normalizedByteLength: safeGet(snapshot, "normalizedByteLength"),
149
- } as CollectorSnapshot)),
150
- evidenceRecords: records(safeGet(value, "evidenceRecords")).map((record) => ({ evidenceId: safeGet(record, "evidenceId"), kind: safeGet(record, "kind"), versionId: safeGet(record, "versionId"), contentDigest: safeGet(record, "contentDigest"), firstObservedAt: safeGet(record, "firstObservedAt"), githubId: safeGet(record, "githubId"), authorLogin: safeGet(record, "authorLogin"), htmlUrl: safeGet(record, "htmlUrl"), authoritativeTime: safeGet(record, "authoritativeTime") } as CollectorEvidenceRecord)),
151
- };
152
- }
153
-
154
- /** Readable projection facts only; omit keys that are the wrong type — never fabricate defaults. */
155
- function projectSubmissionProjection(
156
- raw: unknown,
157
- ): Partial<CollectorSubmissionProjection> | undefined {
158
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return undefined;
159
- const p = raw as Record<string, unknown>;
160
- const out: Partial<CollectorSubmissionProjection> = {};
161
- if (
162
- p["findingsSource"] === "absent" ||
163
- p["findingsSource"] === "array" ||
164
- p["findingsSource"] === "unreadable"
165
- ) {
166
- out.findingsSource = p["findingsSource"];
167
- }
168
- if (typeof p["findingsProjectedCount"] === "number") {
169
- out.findingsProjectedCount = p["findingsProjectedCount"];
170
- }
171
- if (typeof p["findingsUnprojected"] === "boolean") {
172
- out.findingsUnprojected = p["findingsUnprojected"];
173
- }
174
- if (
175
- p["unfinishedReasonsSource"] === "absent" ||
176
- p["unfinishedReasonsSource"] === "array" ||
177
- p["unfinishedReasonsSource"] === "unreadable"
178
- ) {
179
- out.unfinishedReasonsSource = p["unfinishedReasonsSource"];
180
- }
181
- if (typeof p["unfinishedReasonsProjectedCount"] === "number") {
182
- out.unfinishedReasonsProjectedCount = p["unfinishedReasonsProjectedCount"];
183
- }
184
- if (typeof p["unfinishedReasonsUnprojected"] === "boolean") {
185
- out.unfinishedReasonsUnprojected = p["unfinishedReasonsUnprojected"];
186
- }
187
- return Object.keys(out).length > 0 ? out : undefined;
188
- }
@@ -5,7 +5,6 @@
5
5
 
6
6
  import {
7
7
  COLLECTOR_OUTPUT_TOOL,
8
- validateAcceptedCollectorReceipt,
9
8
  type CollectorReceipt,
10
9
  } from "./collector-output.ts";
11
10
  import {
@@ -44,7 +43,6 @@ export {
44
43
  JUDGE_OUTPUT_TOOL_NAME,
45
44
  REVIEWER_OUTPUT_TOOL_NAME,
46
45
  MERGER_OUTPUT_TOOL_NAME,
47
- validateAcceptedCollectorReceipt,
48
46
  validateReviewerIntent,
49
47
  validateAcceptedWorkerDetails,
50
48
  validateDoctorSubmissionShape,
@@ -1,6 +1,5 @@
1
1
  /** Composition-root unique authoritative public-role records (#509 / #524). */
2
2
  import { COLLECTOR_OUTPUT_TOOL } from "./package-contracts/collector-output.ts";
3
- import { COLLECTOR_WAIT_TOOL } from "./collector-ledger.ts";
4
3
  import { GATEKEEPER_OUTPUT_TOOL_NAME } from "./package-contracts/gatekeeper-output.ts";
5
4
  import { NAVIGATOR_OUTPUT_TOOL_NAME } from "./package-contracts/navigator-output.ts";
6
5
  import { JUDGE_OUTPUT_TOOL_NAME } from "./package-contracts/judge-output.ts";
@@ -232,23 +231,18 @@ export const PUBLIC_ROLE_RECORDS = [
232
231
  ],
233
232
  },
234
233
  // ak-collector-repo is GitHub owner/repo identity, not a local material path (#438).
234
+ // #1088: seat + request-manifest kept; code no longer collects — LLM uses host CLI.
235
235
  {
236
236
  role: "collector",
237
237
  inCallAutoResume: false,
238
238
  presentSettled: "always",
239
239
  summonResume: false,
240
240
  admission: "collect-target",
241
- /** Public no_receipt projects a durable bind-target rejection. */
242
- projectTargetBindRejection: true,
243
241
  sameParent: "none",
244
242
  phases: [null],
245
243
  bareCommand: false,
246
244
  outputTool: COLLECTOR_OUTPUT_TOOL,
247
- settlement: "residual",
248
- /** #633: a prior wait-tool residual must not mask this attempt. */
249
- residualScan: "current-attempt",
250
- residualTool: COLLECTOR_WAIT_TOOL,
251
- runnerFailure: "collector-known-first",
245
+ settlement: "sealed",
252
246
  artifactFace: {
253
247
  evidenceRole: true,
254
248
  leaves: [
@@ -264,10 +258,13 @@ export const PUBLIC_ROLE_RECORDS = [
264
258
  { field: "repo", from: "repository.display", flag: "ak-collector-repo" },
265
259
  { field: "pr", from: "prNumber", text: true, flag: "ak-collector-pr" },
266
260
  { field: "requestManifestPath", flag: "ak-collector-request-manifest" },
267
- { field: "waitMs", from: "waitWindowMs", text: true, flag: "ak-collector-wait-ms" },
268
261
  ],
269
262
  activationStage: "load-and-install",
270
- sessionMaterials: ["CLAUDE.md", "souls/collector.md"],
263
+ sessionMaterials: [
264
+ "CLAUDE.md",
265
+ "souls/collector.md",
266
+ "resources/collector-bot-handbook.md",
267
+ ],
271
268
  },
272
269
  {
273
270
  role: "doctor",
@@ -69,7 +69,6 @@ import {
69
69
  parseCollectorRepository,
70
70
  type CollectorRepository,
71
71
  } from "../collector-config.ts";
72
- import { resolveCollectorTarget } from "../collector-target.ts";
73
72
  import { ownerRepoFromGitHubRemoteUrl } from "./github-remote.ts";
74
73
  import {
75
74
  FixerPacketValidationError,
@@ -231,13 +230,11 @@ export type AdmittedFixerInvocation = AdmittedRoleInvocationBase & {
231
230
 
232
231
  export type AdmittedCollectorInvocation = AdmittedRoleInvocationBase & {
233
232
  readonly role: "collector";
234
- /** Bound at admission when explicit/--pr or unique head/commit; otherwise role binds. */
233
+ /** Bound at admission when explicit --pr; otherwise the LLM locates via host CLI. */
235
234
  readonly prNumber?: number;
236
235
  readonly repository: CollectorRepository;
237
236
  readonly requestManifestPath?: string;
238
237
  readonly manifestDigest: string;
239
- /** Wait-window ms (#678 D4); default applied at role activate when omitted. */
240
- readonly waitWindowMs?: number;
241
238
  };
242
239
 
243
240
  export type AdmittedDoctorInvocation = AdmittedRoleInvocationBase & {
@@ -899,7 +896,6 @@ type SeatArgvFields = {
899
896
  prNumber?: number;
900
897
  repo?: string;
901
898
  requestManifestPath?: string;
902
- waitWindowMs?: number;
903
899
  issueNumber?: number;
904
900
  runs?: string;
905
901
  sourceRun?: string;
@@ -975,17 +971,6 @@ function assignPublicSeatOption(
975
971
  case "request-manifest":
976
972
  fields.requestManifestPath = requireOptionPath(taken.def.canonical, value);
977
973
  return;
978
- case "wait-ms": {
979
- if (value === undefined || !/^[1-9]\d*$/.test(value.trim())) {
980
- throw new CliUsageError("--wait-ms requires a positive safe-integer millisecond value");
981
- }
982
- const parsed = Number(value.trim());
983
- if (!Number.isSafeInteger(parsed) || parsed < 1) {
984
- throw new CliUsageError("--wait-ms requires a positive safe-integer millisecond value");
985
- }
986
- fields.waitWindowMs = parsed;
987
- return;
988
- }
989
974
  case "issue": {
990
975
  if (value === undefined || value.trim() === "") {
991
976
  throw new CliUsageError("doctor --issue requires a positive integer");
@@ -1100,7 +1085,6 @@ export function parsePublicSeatArgv(
1100
1085
  ...(fields.prNumber === undefined ? {} : { prNumber: fields.prNumber }),
1101
1086
  ...(fields.repo === undefined ? {} : { repo: fields.repo }),
1102
1087
  ...(fields.requestManifestPath === undefined ? {} : { requestManifestPath: fields.requestManifestPath }),
1103
- ...(fields.waitWindowMs === undefined ? {} : { waitWindowMs: fields.waitWindowMs }),
1104
1088
  ...(fields.issueNumber === undefined ? {} : { issueNumber: fields.issueNumber }),
1105
1089
  ...(fields.runs === undefined ? {} : { runs: fields.runs }),
1106
1090
  ...(fields.sourceRun === undefined ? {} : { sourceRun: fields.sourceRun }),
@@ -1485,7 +1469,6 @@ export type PublicSeatParse = {
1485
1469
  readonly prNumber?: number;
1486
1470
  readonly repo?: string;
1487
1471
  readonly requestManifestPath?: string;
1488
- readonly waitWindowMs?: number;
1489
1472
  readonly issueNumber?: number;
1490
1473
  readonly runs?: string;
1491
1474
  readonly sourceRun?: string;
@@ -1676,24 +1659,17 @@ export async function admitPublicRole(
1676
1659
  attachmentPaths,
1677
1660
  ...project,
1678
1661
  placedFields: async (placed) => {
1679
- // Task materials are frozen before target resolution (#676 A).
1680
- const target = await resolveCollectorTarget({
1681
- projectRoot,
1682
- repository,
1683
- ...(explicitPrNumber === undefined ? {} : { explicitPrNumber }),
1684
- });
1685
- const prNumber = target.kind === "bound" ? target.prNumber : undefined;
1662
+ // #1088: explicit --pr only. Unbound PR is located by the LLM via host CLI.
1686
1663
  let requestManifestPath: string | undefined;
1687
1664
  if (manifestCanonicalJson !== undefined) {
1688
1665
  requestManifestPath = join(placed.runDirectory, "request-manifest.json");
1689
1666
  await writeFile(requestManifestPath, manifestCanonicalJson, "utf8");
1690
1667
  }
1691
1668
  return {
1692
- ...(prNumber === undefined ? {} : { prNumber }),
1669
+ ...(explicitPrNumber === undefined ? {} : { prNumber: explicitPrNumber }),
1693
1670
  repository: repository.canonical,
1694
1671
  repositoryDisplay: repository.display,
1695
1672
  ...(requestManifestPath === undefined ? {} : { requestManifestPath }),
1696
- ...(parsed.waitWindowMs === undefined ? {} : { waitWindowMs: parsed.waitWindowMs }),
1697
1673
  manifestDigest,
1698
1674
  };
1699
1675
  },
@@ -524,13 +524,12 @@ const COLLECTOR_OPTIONS = [
524
524
  canonical: "--pr",
525
525
  aliases: [],
526
526
  valueMetavar: "number",
527
- // #676 D1: optional when context uniquely determines the PR; ambiguous → require explicit.
528
527
  required: false,
529
528
  repeatable: false,
530
529
  form: "option",
531
530
  description: {
532
- en: "Positive GitHub pull request number. Optional when unique branch/HEAD association binds the PR, or when the Collector role can decide the target from task materials via bind-target; multi-candidate git context or a role that cannot decide requires an explicit value.",
533
- zh: "正整数 GitHub PR 号。分支/HEAD 唯一关联可绑定时可省略;亦可由通进司从任务材料经 bind-target 判定。git 多候选或角色无法判定时必须显式提供。",
531
+ en: "Positive GitHub pull request number. Optional when the Collector role locates the PR via host CLI from task materials.",
532
+ zh: "正整数 GitHub PR 号。可选;省略时由通进司经宿主 CLI 从任务材料自行定位。",
534
533
  },
535
534
  },
536
535
  {
@@ -557,22 +556,8 @@ const COLLECTOR_OPTIONS = [
557
556
  repeatable: false,
558
557
  form: "option",
559
558
  description: {
560
- en: "Optional request manifest JSON path ({requests:[{id,body}]}). Role may also decide request bodies from handbook/field activity without this file.",
561
- zh: "可选 request manifest JSON 路径({requests:[{id,body}]})。角色亦可依手册/现场自行判定请求正文,不必依赖本文件。",
562
- },
563
- },
564
- {
565
- id: "wait-ms",
566
- owner: "collector",
567
- canonical: "--wait-ms",
568
- aliases: [],
569
- valueMetavar: "ms",
570
- required: false,
571
- repeatable: false,
572
- form: "option",
573
- description: {
574
- en: "Wait-window duration in milliseconds after the work step opens (default 600000 = 10 minutes). Does not require a code change to adjust.",
575
- zh: "工作步骤开启后的等待窗时长(毫秒;默认 600000=十分钟)。改配置即可,无需改代码。",
559
+ en: "Optional request manifest JSON path ({requests:[{id,body}]}). Parsed for semantics and passed to the Collector as materials — not a machine collection directive.",
560
+ zh: "可选 request manifest JSON 路径({requests:[{id,body}]})。按语义解析后作为材料递给通进司,不是机器代收 finding 的指令。",
576
561
  },
577
562
  },
578
563
  ] as const satisfies readonly PublicOptionDefinition[];