@akagilnc/pi-workflow-roles 0.1.5068 → 0.1.5096

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 (80) hide show
  1. package/dist/acp-host/production-host.js +429 -383
  2. package/dist/atomic-write.js +13 -19
  3. package/dist/auditor-soul.js +2 -1
  4. package/dist/compliance-transport.js +3 -2
  5. package/dist/countersign-contracts.js +2 -2
  6. package/dist/doctor-contracts.js +1 -1
  7. package/dist/gatekeeper-role.js +14 -35
  8. package/dist/headless-host/production-host.js +461 -415
  9. package/dist/inspector-contracts.js +5 -2
  10. package/dist/merger-contracts.js +1 -1
  11. package/dist/migrate-book-topology.js +49 -33
  12. package/dist/navigator-attendance.js +28 -143
  13. package/dist/navigator-invocation-identity.js +2 -0
  14. package/dist/navigator-public-session.js +13 -1
  15. package/dist/navigator-work-base.js +113 -0
  16. package/dist/notary-contracts.js +0 -2
  17. package/dist/open-tool-schema.js +4 -1
  18. package/dist/package-contracts/fixer-output.js +1 -1
  19. package/dist/package-contracts/gatekeeper-output.js +1 -1
  20. package/dist/package-contracts/judge-output.js +2 -2
  21. package/dist/package-contracts/navigator-output.js +1 -1
  22. package/dist/package-contracts/terminating-infrastructure.js +7 -4
  23. package/dist/packaged-role-registry.js +9 -5
  24. package/dist/public-cli/countersign-run.js +1 -1
  25. package/dist/public-cli/invocation.js +6 -3
  26. package/dist/public-cli/main.js +78 -41
  27. package/dist/public-cli/settlement.js +20 -1
  28. package/dist/receipt-delivery-policy.js +9 -2
  29. package/dist/review-submission.js +19 -8
  30. package/dist/submission-errors.js +17 -0
  31. package/dist/submission-ledger.js +18 -6
  32. package/docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md +9 -0
  33. package/extensions/role-runtime.ts +0 -27
  34. package/package.json +2 -1
  35. package/resources/836-deleted-machine-instruction-inventory.md +3 -3
  36. package/resources/collector-bot-handbook.md +4 -0
  37. package/resources/navigator-route-playbook.md +4 -6
  38. package/scripts/build-package.mjs +4 -0
  39. package/souls/coder-output-guide.md +1 -0
  40. package/src/auditor-soul.ts +2 -1
  41. package/src/collector-role.ts +6 -7
  42. package/src/collector-tool-schemas.ts +5 -5
  43. package/src/compliance-transport.ts +3 -2
  44. package/src/countersign-contracts.ts +2 -2
  45. package/src/countersign-role.ts +3 -7
  46. package/src/diarist-role.ts +2 -2
  47. package/src/doctor-contracts.ts +1 -1
  48. package/src/gatekeeper-role.ts +15 -36
  49. package/src/gleaner-left-role.ts +2 -2
  50. package/src/inspector-contracts.ts +5 -2
  51. package/src/inspector-role.ts +1 -1
  52. package/src/judge-role.ts +14 -22
  53. package/src/merger-contracts.ts +1 -1
  54. package/src/navigator-attendance.ts +38 -164
  55. package/src/navigator-invocation-identity.ts +2 -0
  56. package/src/navigator-public-session.ts +17 -1
  57. package/src/navigator-role.ts +4 -0
  58. package/src/navigator-session-contracts.ts +2 -0
  59. package/src/navigator-work-base.ts +123 -0
  60. package/src/notary-contracts.ts +0 -4
  61. package/src/open-tool-schema.ts +9 -4
  62. package/src/package-contracts/fixer-output.ts +1 -1
  63. package/src/package-contracts/gatekeeper-output.ts +3 -3
  64. package/src/package-contracts/judge-output.ts +2 -4
  65. package/src/package-contracts/navigator-output.ts +1 -1
  66. package/src/package-contracts/terminating-infrastructure.ts +12 -10
  67. package/src/packaged-role-registry.ts +5 -1
  68. package/src/public-cli/countersign-run.ts +1 -1
  69. package/src/public-cli/invocation.ts +6 -3
  70. package/src/public-cli/settlement.ts +21 -0
  71. package/src/receipt-delivery-policy.ts +9 -1
  72. package/src/review-submission.ts +24 -10
  73. package/src/reviewer-role.ts +3 -3
  74. package/src/role-runtime-dependencies.ts +1 -5
  75. package/src/role-runtime.ts +61 -63
  76. package/src/secretariat-role.ts +3 -3
  77. package/src/submission-errors.ts +16 -0
  78. package/src/submission-gate.ts +2 -1
  79. package/src/submission-ledger.ts +23 -11
  80. package/src/worker-role.ts +7 -9
@@ -28,7 +28,7 @@ import { DOCTOR_CANDIDATE_ENTRY_TYPE } from "../dossier-resolution.js";
28
28
  import { REVIEWER_OUTPUT_TOOL_NAME, } from "../package-contracts/reviewer-output.js";
29
29
  import { packagedAuditToolName, packagedDurableOfficerEntry, packagedProjectsTargetBindRejection, packagedRoleAcceptedOutputTool, packagedRoleMetadata, packagedSkipsGateOnInfrastructureStage, } from "../packaged-role-registry.js";
30
30
  import { SECRETARIAT_COUNTERSIGN_TERMINAL_FACT_KEY, SECRETARIAT_GATE_OFFICER_ENTRY_TYPE, } from "../secretariat-contracts.js";
31
- import { classifyPackagedRoleTerminalResult, findLatestDurablePackagedRoleTerminal, hasNavigatorInfrastructureFailureBase, isAcceptedPackagedRoleTerminalResult, isReceiptSettlementBindingClear, NAVIGATOR_INVOCATION_ENTRY, parseInvocationMarkerIdentity, } from "../navigator-invocation-identity.js";
31
+ import { classifyPackagedRoleTerminalResult, findLatestDurablePackagedRoleTerminal, hasNavigatorInfrastructureFailureBase, isAcceptedPackagedRoleTerminalResult, isReceiptSettlementBindingClear, NAVIGATOR_INVOCATION_ENTRY, NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY, parseInvocationMarkerIdentity, } from "../navigator-invocation-identity.js";
32
32
  import { NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_TURN_LIMIT, noReceiptLifecycleFacts, parseNoReceiptLifecycleFacts, } from "../receipt-delivery-policy.js";
33
33
  import { roleRunArtifactsDirectory } from "../role-run-placement.js";
34
34
  import { listSeamOwnedUniqueErrorFacePaths, RUN_TERMINAL_ARTIFACT_FILES, RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS, } from "../run-terminal-artifacts.js";
@@ -1610,7 +1610,26 @@ async function withOptionalGateProjection(base, sessionDirectory, gateContext =
1610
1610
  }
1611
1611
  return attachEngineDetourToolUsage(next, sessionDirectory, gateContext);
1612
1612
  }
1613
+ function routePlaybookFailureMessage(entries) {
1614
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
1615
+ const entry = entries[i];
1616
+ if (entry?.type !== "custom" || entry.customType !== NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY)
1617
+ continue;
1618
+ const data = entry.data;
1619
+ if (!isRecord(data) || typeof data.message !== "string" || data.message.trim() === "")
1620
+ return undefined;
1621
+ return data.message;
1622
+ }
1623
+ return undefined;
1624
+ }
1613
1625
  export function extractNavigatorFact(entries) {
1626
+ const fact = extractNavigatorAttendanceFact(entries);
1627
+ if (fact.advisoryDiagnostic !== undefined)
1628
+ return fact;
1629
+ const message = routePlaybookFailureMessage(entries);
1630
+ return message === undefined ? fact : { ...fact, advisoryDiagnostic: message };
1631
+ }
1632
+ function extractNavigatorAttendanceFact(entries) {
1614
1633
  // Affirmative attendance only. Missing / uncorrelated / unparseable is never no-advice.
1615
1634
  // Minimal provenance: latest durable terminal + nearest preceding marker +
1616
1635
  // invocationId + post-terminal order. Marker↔terminal cardinality belongs to
@@ -1,5 +1,4 @@
1
1
  const RECEIPT_DELIVERY_TURN_LIMIT = 2;
2
- const RECEIPT_DELIVERY_PROMPT = "\u672C\u4F1A\u8BDD\u5C1A\u65E0\u5DF2\u63A5\u53D7\u7684 typed \u56DE\u6267\u3002";
3
2
  const NO_RECEIPT_LIFECYCLE_ENTRY_TYPE = "ak-no-receipt-lifecycle";
4
3
  function isRecord(value) {
5
4
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -74,6 +73,15 @@ function createReceiptDeliveryPolicy() {
74
73
  if (accepted) return "accepted";
75
74
  return deliveryTurns < RECEIPT_DELIVERY_TURN_LIMIT ? "request-delivery" : "no-receipt";
76
75
  },
76
+ /** Current typed delivery facts. No prose. */
77
+ deliveryState() {
78
+ return {
79
+ terminalToolCalled,
80
+ rejectedReceipts: rejectedReceipts.map((item) => ({ ...item })),
81
+ deliveryTurns,
82
+ acceptedReceipt: false
83
+ };
84
+ },
77
85
  facts(binding) {
78
86
  return noReceiptLifecycleFacts({ terminalToolCalled, rejectedReceipts: [...rejectedReceipts], deliveryTurns, ...binding });
79
87
  }
@@ -81,7 +89,6 @@ function createReceiptDeliveryPolicy() {
81
89
  }
82
90
  export {
83
91
  NO_RECEIPT_LIFECYCLE_ENTRY_TYPE,
84
- RECEIPT_DELIVERY_PROMPT,
85
92
  RECEIPT_DELIVERY_TURN_LIMIT,
86
93
  createReceiptDeliveryPolicy,
87
94
  noReceiptLifecycleFacts,
@@ -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
- /** Open receipt: the queue reads only status; all other submitted fields ride unchanged. */
7
- export const reviewSubmissionSchema = withTerminatingOutputDeclarations(openToolObject(Type.Object({
8
- status: Type.Optional(Type.Unknown({
9
- description: "converged | continue | escalate — 非三态时请重读后重交,勿改标。",
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
+ * External-dependency failure while the seat can still submit:
12
+ * docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md
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: "审核席本轮裁决的判别状态。",
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
- failOnInfrastructureFailureDeclaration(params, {
484
- failInfrastructure(error, ctx) {
485
- failInfrastructure(error, ctx);
486
- },
487
- }, context, toolCallId, tool.bounceInfrastructureDeclaration);
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) {
@@ -0,0 +1,9 @@
1
+ # 交卷 schema 不把格式当拒收权
2
+
3
+ Status: accepted
4
+
5
+ 交卷工具 schema 保留字段声明与 description;非审核席维持零 required(含记账位)、顶层开放额外属性的原决定。审核席的判别字段 status 则为三态枚举且必填,没有省略 status 的失败例外。审核席仍能交卷、而完成审核所需的外部依赖发生真实基础设施失败时,status 取上呈值,失败细节写入既有 infrastructureFailure;这不是放行或封驳。执行或宿主已失败、审核席无法交卷时,仍由宿主如实失败终局。
6
+
7
+ 包侧收卷:审核席除判别字段外,其余字段不必填、不限制,允许额外属性;声明不是包侧校验授权,包不因格式拒收或判死;判别字段没填或读不出时只说读不出并附收到的值,不在重交提示里另写一套状态名。key 名字不必逐字相同,合不合格由审刑院按是否读得懂来判。
8
+
9
+ 宿主生成端:使用宿主原生能力;Codex strict 结构化输出要求根节点为 object、对象关闭额外属性,不能以该出口承诺模型可生成未声明字段。此项宿主缺口如实报告,不另造适配层或出口;生成端的限制不变成包侧拒收权。(#1055。三态枚举必填并撤销无 status 例外:源卷 e4b50c8e-15c7-4c92-9263-959efadb24eb.jsonl,owner uuid ddff7ae0-0b1e-44eb-b80c-96d2a3d3d239、0ddc3a4e-9a53-4565-b1cc-7ba3471d11e5、1707c6ec-ac17-4876-97d1-73b57c734259。根节点须为 object:[Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs))
@@ -14,14 +14,12 @@ import type { Message } from "@earendil-works/pi-ai";
14
14
  import {
15
15
  registerNavigatorModelCommand,
16
16
  resolveNavigatorAuthorityMaterial,
17
- type NavigatorTargetRole,
18
17
  } from "../src/navigator-attendance.ts";
19
18
  import { loadNavigatorWorkContext as loadHostNeutralNavigatorWorkContext } from "../src/navigator-work-context.ts";
20
19
  export { resolveNavigatorAuthorityMaterial };
21
20
  import { JUDGE_OUTPUT_TOOL_NAME } from "../src/package-contracts/judge-output.ts";
22
21
  import { readOAuthKeepaliveProviders } from "../src/oauth-keepalive.ts";
23
22
  import {
24
- formatNavigatorRoleHelp,
25
23
  type RoleRuntimeDependencies,
26
24
  } from "../src/role-runtime.ts";
27
25
  import { createRoleRuntimeDependencies } from "../src/role-runtime-dependencies.ts";
@@ -31,31 +29,6 @@ if (process.env.AK_ROLE_PACKAGE_ROOT === undefined || process.env.AK_ROLE_PACKAG
31
29
  process.env.AK_ROLE_PACKAGE_ROOT = packageRoot;
32
30
  }
33
31
 
34
- // Cold `pi -e <extension> --help` must cover installed-package process startup under CI load.
35
- // This bound is process-startup budget only — not settlement-to-visible presentation latency.
36
- export const NAVIGATOR_LIVE_HELP_TIMEOUT_MS = 30_000;
37
-
38
- /**
39
- * Subprocess live help (disk-reread via fresh pi -e). Kept for cold-install proofs.
40
- * Production Navigator prepare must not call this on the post-role grace path — under
41
- * concurrent CI load each role's pi --help alone can exceed the accepted 3s grace.
42
- */
43
- export async function loadNavigatorRoleHelp(
44
- pi: Pick<ExtensionAPI, "exec">,
45
- extensionPath: string,
46
- cwd: string,
47
- role: NavigatorTargetRole,
48
- ): Promise<string> {
49
- const result = await pi.exec("pi", ["--no-extensions", "--no-skills", "--no-prompt-templates", "--no-themes", "--no-context-files", "-e", extensionPath, "--ak-role", role, "--help"], { cwd, timeout: NAVIGATOR_LIVE_HELP_TIMEOUT_MS });
50
- if (result.killed) {
51
- throw new Error(`live help unavailable for ${role}: process did not settle`, { cause: result });
52
- }
53
- if (result.code !== 0) throw new Error(`live help unavailable for ${role}: process exited with code ${result.code}`, { cause: result });
54
- return result.stdout || result.stderr;
55
- }
56
-
57
- export { formatNavigatorRoleHelp as formatInProcessNavigatorRoleHelp };
58
-
59
32
  function projectJudgeTranscriptForAudit(messages: Message[]): Message[] {
60
33
  return messages.map((message) => {
61
34
  if (message.role !== "assistant") return message;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.5068",
3
+ "version": "0.1.5096",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -36,6 +36,7 @@
36
36
  "schemas",
37
37
  "packets",
38
38
  "resources",
39
+ "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md",
39
40
  "README.md",
40
41
  "LICENSE",
41
42
  "THIRD_PARTY_NOTICES.md",
@@ -13,8 +13,8 @@
13
13
  | `run-lifecycle.ts` A4.1 | `[ak-role:resume-continue]` | 生产码不再含此字面量。结算只跳过空轮与引擎指针续跑。 |
14
14
  | `run-lifecycle.ts` A4.2 | `重新读 <path>` | 前轮已删改写。 |
15
15
  | `submission-correctable-error.ts` A4.5 | `终局交卷并非本轮唯一工具调用。` | 删。Pi/ACP `deliverSubmissionRejection` 不再注入该句。 |
16
- | `role-runtime.ts` 催交 | `本会话尚无已接受的 typed 回执。` | **留**(轮次/预算,Q2)。 |
17
- | `navigator-attendance.ts` early prepare 开场 | `父衙门进行中。请在本宿主会话熟悉下列材料并待命;结算结果送达后,再给出下一步建议。本轮不要提交最终路线建议。` | #959 迁入 `resources/navigator-route-playbook.md`「到场阶段」;代码只留中立指针句「本轮为待命轮。」 |
18
- | `navigator-attendance.ts` settlement feed 开场 | `父衙门结算已送达。请根据下列材料给出下一步建议。` | #959 迁入同手册;代码只留中立指针句「本轮为结算投喂轮。」 |
16
+ | `role-runtime.ts` 催交 | `本会话尚无已接受的 typed 回执。` | #1021 改为递当前 typed delivery 事实,不写催交句。 |
17
+ | `navigator-attendance.ts` early prepare 开场 | `父衙门进行中。请在本宿主会话熟悉下列材料并待命;结算结果送达后,再给出下一步建议。本轮不要提交最终路线建议。` | #959 迁入 `resources/navigator-route-playbook.md`「到场阶段」。#1021 删掉代码指针句「本轮为待命轮。」:待命不调模型,只记到场。 |
18
+ | `navigator-attendance.ts` settlement feed 开场 | `父衙门结算已送达。请根据下列材料给出下一步建议。` | #959 迁入同手册。#1021 删掉代码指针句「本轮为结算投喂轮。」:结算轮只递当前 typed 三态及指针。 |
19
19
 
20
20
  催交句与 2.1–2.3 打回文、重问三句不在删除列。
@@ -27,6 +27,10 @@
27
27
  - 官方:https://prod.cursor.com/help/ai-features/bugbot 与 automations 文档
28
28
  - 内置 Bugbot 与同账号自定义任务须区分;某任务额度耗尽不推断其他任务失效
29
29
 
30
+ ## 工序
31
+
32
+ 判定任务材料后绑定本仓唯一 PR(prNumber,或由 issueNumber 解析到唯一 PR)。已有显式 PR 绑定时不必再绑。多义或无法确定时不猜,交给调用方明确目标。未绑定前不观察。观察把证据存成不可变快照;上下文里的正文只到头部,其余按 evidenceId 开卷。请求发在所引最新快照 HEAD 上;清单外的 requestId 要带上正文。交卷交 findings 指针,不把模板通知当成 finding;没有 finding 就不要编一条。未完成写现场原因,不写成没问题。
33
+
30
34
  ## 修正规则
31
35
 
32
36
  - 优先引用官方文档;现场差异记入仓库差异条并附 PR/证据指针
@@ -1,11 +1,9 @@
1
- ## 到场阶段(#959 / ADR 0073)
1
+ ## 到场阶段(#959 / ADR 0073 / #1021)
2
2
 
3
- 代码只传中立阶段指针句与 typed 材料;阶段行为说明住本手册,随案附卷。
3
+ 阶段行为说明住本手册。代码不写阶段句,也不把本手册再拼进用户消息。
4
4
 
5
- - **待命轮**:父衙门进行中。请在本宿主会话熟悉下列材料并待命;结算结果送达后,再给出下一步建议。本轮不要提交最终路线建议。
6
- - **结算投喂轮**:父衙门结算已送达。请根据下列材料给出下一步建议。
7
-
8
- Early 待命轮的输出由执行机制丢弃;最终路线建议只在结算投喂轮给出。
5
+ - **待命**:父衙门进行中时,代码只记到场,不调用模型。这一轮没有输出。
6
+ - **结算**:父衙门结算送达后,用户消息只有该次 typed 三态及指针。Soul 与本手册已在系统提示里。根据这份结算和本手册给出下一步建议。
9
7
 
10
8
  ## 宿主轴(与角色命令面)
11
9
 
@@ -15,6 +15,10 @@ const entries = [
15
15
  // (#590: published non-bundle graph must stay closed under its own relative edges).
16
16
  "navigator-public-session",
17
17
  "navigator-session-contracts",
18
+ // Static import of navigator-attendance and navigator-public-session.
19
+ "navigator-work-base",
20
+ // Static import of navigator-work-base.
21
+ "atomic-write",
18
22
  "public-role-summons",
19
23
  "pi/in-process-session",
20
24
  "activation-ledger-git",
@@ -2,4 +2,5 @@
2
2
 
3
3
  提交:标题冠 ak-roles: 在最前;forward commit,严禁 amend——改错就再提交一笔,不改历史。
4
4
  交卷回执:改动清单与票面逐项对得上;验证证据附命令与结果原文。
5
+ completed 回执含四项证据:TDD、同模式、引入回归、行为事实。
5
6
  新造通用件:回执写明仓内、语言标准库、包生态各搜了什么、为什么不用。
@@ -46,8 +46,9 @@ export const AUDITOR_SESSION_MATERIALS = {
46
46
  "souls/judge-auditor.md",
47
47
  "souls/audit-law.md",
48
48
  "souls/quality-law.md",
49
+ "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md",
49
50
  ],
50
- doctor: ["CLAUDE.md", "souls/doctor-auditor.md"],
51
+ doctor: ["CLAUDE.md", "souls/doctor-auditor.md", "docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md"],
51
52
  } as const satisfies Record<
52
53
  AuditorSoulRole,
53
54
  readonly [string, string, ...(readonly string[])]
@@ -351,8 +351,7 @@ export function createCollectorRoleRuntime(
351
351
  pi.registerTool({
352
352
  name: COLLECTOR_BIND_TARGET_TOOL,
353
353
  label: "通进司认票绑定",
354
- description:
355
- "角色判定任务材料后绑定本仓唯一 PR 目标。可提交 prNumber 或 issueNumber(线上关联唯一 PR);显式 --pr 已绑定时无需再调。多义或无法确定时会正确驳回,要求调用方明确 --pr。",
354
+ description: "绑定本仓唯一 PR 目标。",
356
355
  promptSnippet: "绑定角色判定的 issue/PR 目标",
357
356
  parameters: bindSchema,
358
357
  async execute(toolCallId: string, params: BindParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
@@ -433,7 +432,7 @@ export function createCollectorRoleRuntime(
433
432
  pi.registerTool({
434
433
  name: COLLECTOR_OBSERVE_TOOL,
435
434
  label: "通进司观察",
436
- description: "抓取配置目标的完整 GitHub PR 证据,存不可变快照入卷。正文在上下文中只给头部摘录加指针;需要头部之外的正文时,用 ak_collector_read 按 evidenceId 开卷;findings 的拆分与归类由你在交件时完成。目标未绑定前须先 ak_collector_bind_target。",
435
+ description: "抓取配置目标的 GitHub PR 证据。",
437
436
  promptSnippet: "抓取配置目标 PR 证据",
438
437
  parameters: observeSchema,
439
438
  async execute(toolCallId: string, _params: unknown, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
@@ -464,7 +463,7 @@ export function createCollectorRoleRuntime(
464
463
  pi.registerTool({
465
464
  name: COLLECTOR_READ_TOOL,
466
465
  label: "通进司开卷",
467
- description: "按 evidenceId 开卷读取一条已观测材料的全量正文与指针;只在观察头部摘录不足以判读时调用。",
466
+ description: "按 evidenceId 开卷读取一条已观测材料的全量正文与指针。",
468
467
  promptSnippet: "按指针开卷读材料",
469
468
  parameters: readSchema,
470
469
  async execute(toolCallId: string, params: ReadParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
@@ -499,7 +498,7 @@ export function createCollectorRoleRuntime(
499
498
  pi.registerTool({
500
499
  name: COLLECTOR_REQUEST_TOOL,
501
500
  label: "通进司请求",
502
- description: "在所引最新快照 HEAD 发一次请求。requestId 可取配置清单,或角色依手册/现场判定的稳定 id;后者须同时提供 body。",
501
+ description: "发一次请求。",
503
502
  promptSnippet: "发一次评审请求",
504
503
  parameters: requestSchema,
505
504
  async execute(toolCallId: string, params: RequestParams, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
@@ -625,7 +624,7 @@ export function createCollectorRoleRuntime(
625
624
  pi.registerTool({
626
625
  name: COLLECTOR_WAIT_TOOL,
627
626
  label: "通进司等待",
628
- description: "再观察前等待;实际睡眠不超过剩余等待窗。",
627
+ description: "等待。实际睡眠不超过剩余等待窗。",
629
628
  promptSnippet: "等待窗内等待",
630
629
  parameters: waitSchema,
631
630
  async execute(toolCallId: string, params: WaitParams, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
@@ -656,7 +655,7 @@ export function createCollectorRoleRuntime(
656
655
  pi.registerTool({
657
656
  name: COLLECTOR_OUTPUT_TOOL,
658
657
  label: "通进司输出",
659
- description: "观察完成后提交原交卷。正常完工提交空对象 {}(如需报 finding,填 findings 指针数组);仅在基础设施真实失败时才可填 infrastructureFailure,无失败时必须省略该字段。",
658
+ description: "提交通进司回执。",
660
659
  promptSnippet: "提交通进司回执",
661
660
  parameters: outputSchema,
662
661
  async execute(toolCallId: string, params: OutputParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
@@ -66,10 +66,10 @@ export const collectorOpenWaitWindowArgsSchema = Type.Object({
66
66
  */
67
67
  export const collectorBindTargetArgsSchema = Type.Object({
68
68
  prNumber: Type.Optional(Type.Unknown({
69
- description: "角色判定的本仓 PR 号(正整数)。与 issueNumber 二选一或同指唯一目标。形状指引,非 schema 闸。",
69
+ description: "本仓 PR 号(正整数)。",
70
70
  })),
71
71
  issueNumber: Type.Optional(Type.Unknown({
72
- description: "角色判定的本仓 issue 号(正整数);runtime 经线上关联解析唯一 PR。形状指引,非 schema 闸。",
72
+ description: "本仓 issue 号(正整数)。",
73
73
  })),
74
74
  }, { additionalProperties: true });
75
75
 
@@ -96,7 +96,7 @@ const collectorFindingItemDeclaration = (() => {
96
96
  {
97
97
  additionalProperties: true,
98
98
  description:
99
- "单条 finding 指针:evidenceId + 可选 category/summary。形状指引,非 schema 闸。",
99
+ "单条 finding 指针:evidenceId,以及可选 category、summary。",
100
100
  },
101
101
  );
102
102
  (item as unknown as { required: string[] }).required = [];
@@ -113,12 +113,12 @@ export const collectorOutputBaseSchema = openToolObject(
113
113
  // Nested item declarations ride `items` for registration preservation (ADR 0057).
114
114
  findings: Type.Unsafe({
115
115
  description:
116
- "本次收集到的逐条 findings(指针数组为规范形)。零 finding 的模板通知不得进入;正常完工无 finding 时省略。形状指引,非 schema 闸。",
116
+ "本次收集到的逐条 findings 指针。",
117
117
  items: collectorFindingItemDeclaration,
118
118
  }),
119
119
  unfinishedReasons: Type.Unknown({
120
120
  description:
121
- "未完成原因字符串数组(额度/故障/等待届满等现场依据);不得把未完成表述为无问题。无可报告时省略。形状指引,非 schema 闸。",
121
+ "未完成原因。",
122
122
  }),
123
123
  }),
124
124
  );
@@ -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 { OFFICER_CONCLUSION_REASK } from "./gatekeeper-role.ts";
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 = OFFICER_CONCLUSION_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 (["converged", "continue", "escalate"].includes(status)) {
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");
@@ -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
- // #836 (2026-09-11 御批 / ADR 0003 Amendment): registered-to-provider closed
15
- // value domains reject an unknown status before the submission
16
- // ledger ever records it, defeating 读不出三态→resume 说话者本人. Kept open
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;
@@ -16,14 +16,14 @@ export type { DiaristOutput };
16
16
  export { validateRecordedDiaristOutput };
17
17
 
18
18
  /**
19
- * 起居郎交卷形状;形状指引,非 schema 闸。
19
+ * 起居郎交卷形状。
20
20
  * #901:交边界(sessions);正文与不可解析原字节由机械投影。
21
21
  */
22
22
  export const diaristOutputSchema = withTerminatingOutputDeclarations(
23
23
  openToolObject(
24
24
  Type.Object({
25
25
  status: Type.Unknown({
26
- description: "completed | escalate — 形状指引,非 schema 闸",
26
+ description: "completed | escalate",
27
27
  }),
28
28
  ticketNumber: Type.Optional(
29
29
  Type.Unknown({
@@ -73,7 +73,7 @@ const caseIdentity = Type.Object({ issueNumber: Type.Optional(Type.Integer()), r
73
73
  // (src/countersign-role.ts) — one shared description across both variants
74
74
  // so openToolObjectFromUnion's identical-declaration collapse drops none of it.
75
75
  const DOCTOR_STATUS_DESCRIPTION =
76
- "completed | refused — 形状指引,非 schema 闸;completed 允许空 findings;refused 仅当证据不足以支撑如实案证词" as const;
76
+ "completed | refused。completed 允许空 findings;refused 仅当证据不足以支撑如实案证词" as const;
77
77
  const doctorSubmissionVariants = Type.Union([
78
78
  Type.Object({
79
79
  status: Type.Unknown({ description: DOCTOR_STATUS_DESCRIPTION }),
@@ -4,12 +4,11 @@ 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
- gatekeeperDecisionSchema,
13
12
  gatekeeperOutputSchema,
14
13
  } from "./package-contracts/gatekeeper-output.ts";
15
14
  import type { PublicSummonResult } from "./public-role-summons.ts";
@@ -66,6 +65,8 @@ export type GatekeeperResult =
66
65
  readonly status: "needs_reask";
67
66
  readonly officer: GateOfficer;
68
67
  readonly receipt: unknown;
68
+ /** Discriminator the queue already read. Not written back onto the receipt. */
69
+ readonly receivedStatus?: unknown;
69
70
  readonly runId?: string;
70
71
  }
71
72
  | { readonly status: "no_receipt"; readonly stage: GateOfficer; readonly reason: string; readonly facts: NoReceiptLifecycleFacts }
@@ -159,7 +160,7 @@ export type SubmissionGateHostActions = {
159
160
  export const GATEKEEPER_TOOL_SPEC = {
160
161
  name: GATEKEEPER_OUTPUT_TOOL_NAME,
161
162
  label: "门下省决议",
162
- description: "门下省终局决议,状态为 dispatch 或 pass。",
163
+ description: "门下省终局决议。status 为 dispatch 或 pass。",
163
164
  promptSnippet: "门下省决议",
164
165
  parameters: gatekeeperOutputSchema,
165
166
  } as const;
@@ -168,22 +169,6 @@ export type GatekeeperRuntimeDependencies = {
168
169
  loadSoul(): Promise<string>;
169
170
  };
170
171
 
171
- function result(content: string, details: unknown) {
172
- return { content: [{ type: "text" as const, text: content }], details };
173
- }
174
-
175
- /** Gatekeeper province decision tool — open transport; package-contract projection owns legality. */
176
- export function createGatekeeperOutputTool() {
177
- return {
178
- name: GATEKEEPER_OUTPUT_TOOL_NAME,
179
- description: "提交门下省派官决定。",
180
- parameters: gatekeeperDecisionSchema,
181
- async execute(_id: string, args: unknown) {
182
- return result(`已收 ${String((args as { status?: unknown })?.status)}`, args);
183
- },
184
- };
185
- }
186
-
187
172
  function failureReason(error: unknown): string {
188
173
  if (error instanceof AggregateError) return error.errors.map(failureReason).join("; ");
189
174
  return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
@@ -210,12 +195,9 @@ function readRecord(value: unknown): Record<string, unknown> | undefined {
210
195
  * `fallbackStatus` is the terminal outcome.status when the receipt body has no status key
211
196
  * (keeps missing-args sentinel intact as the receipt).
212
197
  */
213
- /** Map the shared review verdict onto the gate queue words (#1028). */
198
+ /** Map the shared review verdict onto the gate queue words (#1028 / #1055). */
214
199
  function reviewQueueStatus(status: string): string | undefined {
215
- if (status === "converged") return "converged";
216
- if (status === "continue") return "continue";
217
- if (status === "escalate") return "escalate";
218
- return undefined;
200
+ return REVIEW_QUEUE_STATUSES.has(status) ? status : undefined;
219
201
  }
220
202
 
221
203
  function projectOfficerDecision(
@@ -235,7 +217,7 @@ function projectOfficerDecision(
235
217
  if (queueStatus === "continue" || queueStatus === "escalate") {
236
218
  return { status: queueStatus, officer, receipt };
237
219
  }
238
- return { status: "needs_reask", officer, receipt };
220
+ return { status: "needs_reask", officer, receipt, receivedStatus: status };
239
221
  }
240
222
 
241
223
  /**
@@ -371,12 +353,14 @@ function projectOfficerTerminal(
371
353
  summoned,
372
354
  );
373
355
  }
356
+ const receipt = thisCourt.length > 0 ? thisCourt[thisCourt.length - 1] : retainedReceipt(outcome);
374
357
  return withOfficerRunId(
375
358
  {
376
359
  status: "needs_reask",
377
360
  officer,
378
361
  // This-court receipt only (#879).
379
- receipt: thisCourt.length > 0 ? thisCourt[thisCourt.length - 1] : retainedReceipt(outcome),
362
+ receipt,
363
+ receivedStatus: receivedDiscriminator(receipt, "status"),
380
364
  },
381
365
  summoned,
382
366
  );
@@ -391,16 +375,11 @@ export type GatekeeperProjection = {
391
375
  };
392
376
 
393
377
  /**
394
- * Plain-language re-ask when the officer conclusion is not converged|continue|escalate.
395
- * Not a packaged engine handbook line (#755 exception for 读不出三态).
396
- * Every review officer uses the same `status` field and three queue words (#1028).
378
+ * Officer conclusion was missing or not a queue word (#1055).
379
+ * `received` is the status value only, not the rest of the receipt.
397
380
  */
398
- export const OFFICER_CONCLUSION_REASK =
399
- "上次交卷的 status 不是 converged、continue、escalate 三态之一。请重新输出,status 写明其一;continue 或 escalate 的话就是给对方看的原文。" as const;
400
-
401
- /** Pick the re-ask line for the officer under review. */
402
- export function officerConclusionReask(officer: GateOfficer): string {
403
- return OFFICER_CONCLUSION_REASK;
381
+ export function officerConclusionReask(received: unknown): string {
382
+ return unreadableDiscriminatorNotice("status", received);
404
383
  }
405
384
 
406
385
  /**
@@ -17,12 +17,12 @@ 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
- /** 左拾遗弹章交卷形状;形状指引,非 schema 闸。 */
20
+ /** 左拾遗弹章交卷形状。 */
21
21
  export const gleanerLeftOutputSchema = withTerminatingOutputDeclarations(
22
22
  openToolObject(
23
23
  Type.Object({
24
24
  status: Type.Unknown({
25
- description: "completed — 形状指引,非 schema 闸",
25
+ description: "completed",
26
26
  }),
27
27
  findings: Type.Array(
28
28
  Type.Object(