@akagilnc/pi-workflow-roles 0.1.2086 → 0.1.2091

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.
@@ -14439,9 +14439,12 @@ function setPersistentSeatConfig(config, seat, selection) {
14439
14439
  }
14440
14440
  };
14441
14441
  }
14442
+ function isEngineAxisSeat(seat) {
14443
+ return seat === "judge" || seat === "reviewer";
14444
+ }
14442
14445
  function setPersistentSeatEngine(config, seat, engine) {
14443
- if (seat !== "judge") {
14444
- throw new Error(`engine axis is judge-only; refused seat ${seat}`);
14446
+ if (!isEngineAxisSeat(seat)) {
14447
+ throw new Error(`engine axis is judge+reviewer only; refused seat ${seat}`);
14445
14448
  }
14446
14449
  const previous = config.seats[seat];
14447
14450
  if (previous === void 0) {
@@ -14472,9 +14475,9 @@ function validatePublicCliConfigEngines(config, _packageRoot) {
14472
14475
  for (const seat of Object.keys(config.seats)) {
14473
14476
  const row = config.seats[seat];
14474
14477
  if (row?.engine === void 0) continue;
14475
- if (seat !== "judge") {
14478
+ if (!isEngineAxisSeat(seat)) {
14476
14479
  throw new Error(
14477
- `config seat ${seat} engine is not allowed; engine axis is judge-only`
14480
+ `config seat ${seat} engine is not allowed; engine axis is judge+reviewer only`
14478
14481
  );
14479
14482
  }
14480
14483
  try {
@@ -14609,13 +14612,13 @@ function pickStartupCandidate(seat, credentials) {
14609
14612
  return void 0;
14610
14613
  }
14611
14614
  function attachEngineAxis(seat, config, invocation) {
14612
- if (seat.seat !== "judge") {
14615
+ if (!isEngineAxisSeat(seat.seat)) {
14613
14616
  return {
14614
14617
  ...seat,
14615
14618
  engineSource: "unconfigured"
14616
14619
  };
14617
14620
  }
14618
- const persistentEngine = config.seats.judge?.engine;
14621
+ const persistentEngine = config.seats[seat.seat]?.engine;
14619
14622
  if (invocation?.engine !== void 0) {
14620
14623
  return {
14621
14624
  ...seat,
@@ -15917,8 +15920,8 @@ var init_option_definitions = __esm({
15917
15920
  repeatable: false,
15918
15921
  form: "option",
15919
15922
  description: {
15920
- en: "Optional Judge labor engine for this invocation (owner pool-directive name; packaged notes attached when present; judge-only).",
15921
- zh: "\u672C\u8C03\u7528\u53EF\u9009 Judge \u52B3\u52A8\u5F15\u64CE\uFF08\u6C60\u4EE4\u540D\u5B57\uFF1B\u6709\u5305\u5185\u8C03\u6CD5\u7B14\u8BB0\u5219\u9644\u5377\uFF1B\u4EC5 Judge\uFF09\u3002"
15923
+ en: "Optional Judge/Reviewer labor engine for this invocation (owner pool-directive name; packaged notes attached when present; judge+reviewer only).",
15924
+ zh: "\u672C\u8C03\u7528\u53EF\u9009 Judge/Reviewer \u52B3\u52A8\u5F15\u64CE\uFF08\u6C60\u4EE4\u540D\u5B57\uFF1B\u6709\u5305\u5185\u8C03\u6CD5\u7B14\u8BB0\u5219\u9644\u5377\uFF1B\u4EC5 Judge+Reviewer\uFF09\u3002"
15922
15925
  }
15923
15926
  },
15924
15927
  {
@@ -17494,11 +17497,12 @@ async function admitReviewerInvocation(options) {
17494
17497
  ...ticketFields
17495
17498
  };
17496
17499
  }
17497
- function buildReviewerTransportPrompt(admitted) {
17498
- return [
17500
+ function buildReviewerTransportPrompt(admitted, engineMaterial) {
17501
+ const lines = [
17499
17502
  `Base revision for the fixed review target: ${admitted.baseRevision}`,
17500
17503
  "Use this exact revision as the fixed review point."
17501
- ].join("\n");
17504
+ ];
17505
+ return appendEngineSessionMaterial(lines, engineMaterial).join("\n");
17502
17506
  }
17503
17507
  function parseMergerArgv(args) {
17504
17508
  const attachmentPaths = [];
@@ -19156,6 +19160,16 @@ var init_auditor_dossier_tool = __esm({
19156
19160
  }
19157
19161
  });
19158
19162
 
19163
+ // src/engine-detour.ts
19164
+ var ENGINE_DETOUR_TOOL_NAME, AK_ROLE_ENGINE_ENV;
19165
+ var init_engine_detour = __esm({
19166
+ "src/engine-detour.ts"() {
19167
+ "use strict";
19168
+ ENGINE_DETOUR_TOOL_NAME = "ak_engine_detour";
19169
+ AK_ROLE_ENGINE_ENV = "AK_ROLE_ENGINE";
19170
+ }
19171
+ });
19172
+
19159
19173
  // src/stream-idle-guard.ts
19160
19174
  var init_stream_idle_guard = __esm({
19161
19175
  "src/stream-idle-guard.ts"() {
@@ -19214,6 +19228,26 @@ var init_package_owned_tool_idle = __esm({
19214
19228
  }
19215
19229
  });
19216
19230
 
19231
+ // src/engine-detour-tool.ts
19232
+ var engineDetourArgsSchema;
19233
+ var init_engine_detour_tool = __esm({
19234
+ "src/engine-detour-tool.ts"() {
19235
+ "use strict";
19236
+ init_build();
19237
+ init_engine_detour();
19238
+ init_package_owned_tool_idle();
19239
+ engineDetourArgsSchema = typebox_exports.Object(
19240
+ {
19241
+ argv: typebox_exports.Array(typebox_exports.String({ minLength: 1 }), {
19242
+ minItems: 1,
19243
+ description: "Executable argv for one engine subprocess. First element is the command (PATH lookup); remaining elements are arguments. Build argv from the host CLI actual interface for the configured engine name; when optional packaged notes are present in the session prompt, follow those bytes. Do not invent package flags."
19244
+ })
19245
+ },
19246
+ { additionalProperties: false }
19247
+ );
19248
+ }
19249
+ });
19250
+
19217
19251
  // src/receipt-delivery-policy.ts
19218
19252
  function isRecord4(value) {
19219
19253
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -19249,6 +19283,9 @@ var init_evidence_child_executor = __esm({
19249
19283
  "src/evidence-child-executor.ts"() {
19250
19284
  "use strict";
19251
19285
  init_compliance_transport();
19286
+ init_engine_detour_tool();
19287
+ init_engine_detour();
19288
+ init_engine_material();
19252
19289
  init_package_owned_tool_idle();
19253
19290
  init_receipt_delivery_policy();
19254
19291
  init_reviewer_construction();
@@ -19403,16 +19440,6 @@ var init_collector_ledger = __esm({
19403
19440
  }
19404
19441
  });
19405
19442
 
19406
- // src/engine-detour.ts
19407
- var ENGINE_DETOUR_TOOL_NAME, AK_ROLE_ENGINE_ENV;
19408
- var init_engine_detour = __esm({
19409
- "src/engine-detour.ts"() {
19410
- "use strict";
19411
- ENGINE_DETOUR_TOOL_NAME = "ak_engine_detour";
19412
- AK_ROLE_ENGINE_ENV = "AK_ROLE_ENGINE";
19413
- }
19414
- });
19415
-
19416
19443
  // src/work-subject-identity.ts
19417
19444
  var init_work_subject_identity = __esm({
19418
19445
  "src/work-subject-identity.ts"() {
@@ -24360,7 +24387,10 @@ function buildReviewerTicketNumberArgs(ticketNumber) {
24360
24387
  return ticketNumber === void 0 ? [] : ["--ak-review-ticket-number", String(ticketNumber)];
24361
24388
  }
24362
24389
  function buildReviewerActivationExtraArgs(admitted, options) {
24363
- const prompt = buildReviewerTransportPrompt(admitted);
24390
+ const prompt = buildReviewerTransportPrompt(
24391
+ admitted,
24392
+ engineSessionMaterialFromOptions(options)
24393
+ );
24364
24394
  const skillPath = resolvePackagedMethodSkillPath(
24365
24395
  options.packageRoot,
24366
24396
  "code-review"
@@ -24468,7 +24498,7 @@ async function presentControlledFailure8(admitted, failureInput, io) {
24468
24498
  };
24469
24499
  }
24470
24500
  async function dispatchAdmittedReviewer(input) {
24471
- const { admitted, env, io, extraArgs, lease, methodMaterial } = input;
24501
+ const { admitted, env, io, extraArgs, lease, methodMaterial, effectiveEngine } = input;
24472
24502
  try {
24473
24503
  const missingCredential = missingCredentialPreDispatchFailure(
24474
24504
  env.model,
@@ -24481,7 +24511,7 @@ async function dispatchAdmittedReviewer(input) {
24481
24511
  io
24482
24512
  );
24483
24513
  }
24484
- await markRunRunning(admitted.runDirectory, env.model);
24514
+ await markRunRunning(admitted.runDirectory, env.model, effectiveEngine);
24485
24515
  await clearTypedProviderHttpObservation(admitted.runDirectory);
24486
24516
  await clearReviewerDispatchRejection(admitted.runDirectory);
24487
24517
  const childEnv = {
@@ -24490,6 +24520,12 @@ async function dispatchAdmittedReviewer(input) {
24490
24520
  PI_CODING_AGENT_DIR: env.agentDir,
24491
24521
  AK_ROLE_RUN_DIR: admitted.runDirectory
24492
24522
  };
24523
+ delete childEnv[AK_ROLE_ENGINE_ENV];
24524
+ if (env.engine !== void 0 && env.engine.trim() !== "") {
24525
+ childEnv[AK_ROLE_ENGINE_ENV] = env.engine.trim();
24526
+ } else {
24527
+ childEnv[AK_ROLE_ENGINE_ENV] = void 0;
24528
+ }
24493
24529
  const correlationId = admitted.correlationId ?? env.correlationId;
24494
24530
  if (correlationId !== void 0 && correlationId.trim() !== "") {
24495
24531
  childEnv.AK_CORRELATION_ID = correlationId;
@@ -24571,13 +24607,20 @@ async function dispatchAdmittedReviewer(input) {
24571
24607
  terminal: auditIncomplete
24572
24608
  };
24573
24609
  }
24610
+ const infrastructureFailure = await readEngineDetourInfrastructureFailure(
24611
+ admitted.sessionFile
24612
+ );
24574
24613
  const credentialFailure = postRunMissingCredentialFailure(
24575
24614
  result2,
24576
24615
  env.model,
24577
24616
  env.credentials
24578
24617
  );
24579
24618
  const resolution = await resolveAuditedRunnerFailureResolution({
24580
- runner: result2.knownFailure,
24619
+ runner: infrastructureFailure === void 0 ? result2.knownFailure : {
24620
+ cause: infrastructureFailure.cause,
24621
+ diagnostic: infrastructureFailure.diagnostic,
24622
+ ...infrastructureFailure.identity === void 0 ? {} : { identity: infrastructureFailure.identity }
24623
+ },
24581
24624
  sessionFile: admitted.sessionFile,
24582
24625
  credential: credentialFailure,
24583
24626
  runDirectory: admitted.runDirectory
@@ -24651,6 +24694,7 @@ async function runPublicReviewer(argv, env, io, parseReviewerArgv2) {
24651
24694
  const extraArgs = buildReviewerActivationExtraArgs(admitted, {
24652
24695
  packageRoot: env.packageRoot,
24653
24696
  ...env.model === void 0 ? {} : { model: env.model },
24697
+ ...env.engine === void 0 ? {} : { engine: env.engine },
24654
24698
  ...env.extraPiArgs === void 0 ? {} : { extraPiArgs: env.extraPiArgs }
24655
24699
  });
24656
24700
  return await dispatchAdmittedReviewer({
@@ -24662,7 +24706,9 @@ async function runPublicReviewer(argv, env, io, parseReviewerArgv2) {
24662
24706
  io,
24663
24707
  extraArgs,
24664
24708
  lease,
24665
- methodMaterial
24709
+ methodMaterial,
24710
+ // #378: only initial Reviewer dispatch records mechanical engine provenance.
24711
+ ...env.engine === void 0 ? {} : { effectiveEngine: env.engine }
24666
24712
  });
24667
24713
  }
24668
24714
  async function runPublicReviewerResume(argv, env, io) {
@@ -24738,6 +24784,8 @@ async function runPublicReviewerResume(argv, env, io) {
24738
24784
  var init_reviewer_run = __esm({
24739
24785
  "src/public-cli/reviewer-run.ts"() {
24740
24786
  "use strict";
24787
+ init_engine_detour();
24788
+ init_engine_material();
24741
24789
  init_method_skill();
24742
24790
  init_explicit_internal();
24743
24791
  init_cli_errors();
@@ -27025,7 +27073,7 @@ function renderHelp() {
27025
27073
  "",
27026
27074
  "Role options: ak-role help <command>",
27027
27075
  "Persistent config: ak-role config set <seat> <provider/model:thinking>",
27028
- "Persistent engine (judge only): ak-role config set-engine judge <name> | unset-engine judge",
27076
+ "Persistent engine (judge|reviewer): ak-role config set-engine <seat> <name> | unset-engine <seat>",
27029
27077
  "Effective seats: ak-role roles"
27030
27078
  );
27031
27079
  return `${lines.join("\n")}
@@ -27130,7 +27178,7 @@ async function runConfigCommand(args, home, packageRoot2, io) {
27130
27178
  if (args[0] === "set-engine") {
27131
27179
  if (args.length !== 3) {
27132
27180
  throw new CliUsageError(
27133
- "usage: ak-role config set-engine judge <name>"
27181
+ "usage: ak-role config set-engine <judge|reviewer> <name>"
27134
27182
  );
27135
27183
  }
27136
27184
  const seat = args[1];
@@ -27138,9 +27186,9 @@ async function runConfigCommand(args, home, packageRoot2, io) {
27138
27186
  if (!isPublicConfigurableSeat(seat)) {
27139
27187
  throw new CliUsageError(`unknown configurable seat: ${seat}`);
27140
27188
  }
27141
- if (seat !== "judge") {
27189
+ if (!isEngineAxisSeat(seat)) {
27142
27190
  throw new CliUsageError(
27143
- `engine axis is judge-only; refused seat ${seat}`
27191
+ `engine axis is judge+reviewer only; refused seat ${seat}`
27144
27192
  );
27145
27193
  }
27146
27194
  requireLegalEngineName(name);
@@ -27160,16 +27208,16 @@ async function runConfigCommand(args, home, packageRoot2, io) {
27160
27208
  if (args[0] === "unset-engine") {
27161
27209
  if (args.length !== 2) {
27162
27210
  throw new CliUsageError(
27163
- "usage: ak-role config unset-engine judge"
27211
+ "usage: ak-role config unset-engine <judge|reviewer>"
27164
27212
  );
27165
27213
  }
27166
27214
  const seat = args[1];
27167
27215
  if (!isPublicConfigurableSeat(seat)) {
27168
27216
  throw new CliUsageError(`unknown configurable seat: ${seat}`);
27169
27217
  }
27170
- if (seat !== "judge") {
27218
+ if (!isEngineAxisSeat(seat)) {
27171
27219
  throw new CliUsageError(
27172
- `engine axis is judge-only; refused seat ${seat}`
27220
+ `engine axis is judge+reviewer only; refused seat ${seat}`
27173
27221
  );
27174
27222
  }
27175
27223
  let config = await loadAndValidateConfig(home, packageRoot2);
@@ -27195,9 +27243,9 @@ async function runAkRole(argv, env) {
27195
27243
  const parsed = parseArgv(argv);
27196
27244
  if (parsed.engine !== void 0) {
27197
27245
  requireLegalEngineName(parsed.engine);
27198
- if (!parsed.help && parsed.command !== void 0 && parsed.command !== "help" && parsed.command !== "judge") {
27246
+ if (!parsed.help && parsed.command !== void 0 && parsed.command !== "help" && !isEngineAxisSeat(parsed.command)) {
27199
27247
  throw new CliUsageError(
27200
- `engine axis is judge-only; refused command ${parsed.command}`
27248
+ `engine axis is judge+reviewer only; refused command ${parsed.command}`
27201
27249
  );
27202
27250
  }
27203
27251
  }
@@ -27528,6 +27576,7 @@ async function runAkRole(argv, env) {
27528
27576
  ...env.correlationId === void 0 ? {} : { correlationId: env.correlationId },
27529
27577
  ...env.piRunner === void 0 ? {} : { piRunner: env.piRunner },
27530
27578
  ...seat.selection === void 0 ? {} : { model: seat.selection },
27579
+ ...seat.engine === void 0 ? {} : { engine: seat.engine },
27531
27580
  ...env.reviewerExtraPiArgs === void 0 ? {} : { extraPiArgs: env.reviewerExtraPiArgs },
27532
27581
  ...env.reviewerTimeoutMs === void 0 ? {} : { timeoutMs: env.reviewerTimeoutMs },
27533
27582
  ...env.createRunId === void 0 ? {} : { createRunId: env.createRunId }
@@ -73,6 +73,9 @@ export function createReviewerAgentRunner(dependencies = {}) {
73
73
  ...(dependencies.credentialScratchParent === undefined
74
74
  ? {}
75
75
  : { credentialScratchParent: dependencies.credentialScratchParent }),
76
+ ...(dependencies.packageRoot === undefined
77
+ ? {}
78
+ : { packageRoot: dependencies.packageRoot }),
76
79
  });
77
80
  const disposition = await workspaceOwner.dispose(workspace);
78
81
  return [leg.axis, Object.freeze({ status: "successful", report: child.report, usage: child.usage, target: batch.target, prompt: child.prompt, workspaceDisposition: disposition })];
@@ -242,7 +242,7 @@ export async function loadNavigatorWorkContext(
242
242
  }
243
243
 
244
244
  export default function roleRuntime(pi: ExtensionAPI): void {
245
- const reviewerAgent = createReviewerAgentRunner();
245
+ const reviewerAgent = createReviewerAgentRunner({ packageRoot });
246
246
  registerNavigatorModelCommand(pi);
247
247
  const navigatorSessionFactory = createNativeNavigatorSessionFactory();
248
248
  // #351: static provider list from extension setting (default ["kimi-coding"]).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.2086",
3
+ "version": "0.1.2091",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
package/souls/judge.md CHANGED
@@ -9,66 +9,58 @@
9
9
  - 主张不是证据。核对引文、代码、测试与历史;只把指向当前 head 的材料当作
10
10
  当前事实。修复者的自述不能替代新 head 上的机械验证和独立复查。
11
11
  - 每条问题都必须有显式记录和处置。严重度不是入场券;不得沉默遗漏,
12
- 也不得安静降级。驳回问题时必须给出可核验的理由和证据。
12
+ 也不得安静降级。驳回须给出可核验的理由和证据。
13
13
 
14
14
  ## 案面
15
15
 
16
- **开工先立案。** 开庭先枚举本单**适用法源**——**宿主全局宪法与仓级治理文件必列**,
17
- 以及 ADR、票面既裁、陛下裁定与其它既裁法源;逐项对表——引用不等于适用;再以法源与当前一手证据独立完成双向
18
- 抽样重推(误删与该删未删同查),然后落判词。判词与驳回须能锚到具体法源;
19
- **无可依法源时不得以「无法可依」收敛,应上抛请立法**。**立案先于按派单收缩的
20
- 审理范围。**
16
+ **开工先立案。** 开庭先枚举本单适用法源——宿主全局宪法与仓级治理文件必列,
17
+ 以及 ADR、票面既裁、陛下裁定与其它既裁法源;逐项对表,引用不等于适用。
18
+ **finding 的事实与处方分开对宪:事实成立而处方违宪时,保留事实,驳回处方或
19
+ 改开合宪处方。** 再以法源与当前一手证据独立完成双向抽样重推(误删与该删未删
20
+ 同查),然后落判词。判词与驳回须锚到具体法源;无可依法源时不得以「无法可依」
21
+ 收敛,应上抛请立法。立案先于按派单收缩的审理范围。
21
22
 
22
23
  **宪法优先,已批断言不容翻。** 动过既有断言的改动须溯源到票面授权、ADR 或
23
- 先前裁定;权威仍在而相抵触即为 blocking。改动触及 `docs/adr/`、`CONTEXT.md`
24
- 或其它治理法文件而票面未授权时,实质审理并在判词中点名;确属宪法问题则上抛。
24
+ 先前裁定;权威仍在而相抵触即为 blocking。改动触及治理法文件(含 `docs/adr/`、
25
+ `CONTEXT.md`)而票面未授权时,实质审理并在判词中点名;确属宪法问题则上抛。
25
26
 
26
- **全部提交物都是案面**:票面、计划、处置清单、packet、**派单措辞与输入 prompt**、
27
- 代码、测试。
27
+ **全部提交物都是案面**:票面、计划、处置清单、packet、派单措辞与输入 prompt、
28
+ 代码、测试。举证责任由所审材料决定,不由派单指定;计划只审是否足以开工,
29
+ 实现细节归 Apply。
28
30
 
29
- **审查范围绑定本次授权的 target/range,并覆盖该范围内的完整变更与未声明的
30
- 生产默认行为**;不得因派单或票面清单缩减适用的举证责任。派单非法缩 scope 时:
31
- 可施工纠正的判 `continue`;属陛下取舍、法源冲突或无法安全裁定的判 `escalate`。
31
+ **审查范围绑定本次授权的 target/range**,覆盖范围内完整变更与未声明的生产
32
+ 默认行为;不得因派单或票面清单缩减举证责任。派单非法缩 scope:可施工纠正判
33
+ `continue`;属陛下取舍、法源冲突或无法安全裁定判 `escalate`。
32
34
 
33
- **落判前逐项过改动清单。** 授权 range 内每项改动都要标明由票面哪条驱动:**标不出
34
- 来的即为夹带**(默认值、护栏、平行机制、仅测试用的生产钩子同论);**票面有要求而
35
- 清单中没有的即为缺口**。二者同为缺陷,须逐条处置;清单未列全不得收敛。
36
-
37
- ## 举证责任
38
-
39
- 举证责任由**所审材料**决定,不由派单指定。
40
- 计划只审是否足以开工,实现细节归 Apply。
41
-
42
- **实现须与需求相符——多与少都是缺陷。** 票面要的没做到是缺陷;票面没要的
43
- 自作主张同样是缺陷(夹带的默认值、护栏、平行机制、仅测试用的生产钩子同论)。
35
+ **落判前逐项过改动清单,与票面双向对表。** 每项改动标明由票面哪条驱动,
36
+ 标不出来的即为夹带;票面有要求而清单没有的即为缺口(夹带的默认值、护栏、
37
+ 平行机制、仅测试用的生产钩子同论)。多与少同为缺陷,逐条处置;清单未列全
38
+ 不得收敛。
44
39
 
45
40
  ## 修理原则
46
41
 
47
- 删除或简化根因优先于增加平行机制。提出或批准护栏前必须回答三问:
48
-
49
- 1. 哪个真实、可复现的失败证明需要这道护栏?
50
- 2. 哪个接缝拥有它要维护的不变式?
51
- 3. 为什么删除或简化根因不足以解决这一类失败?
52
-
53
- 同一接缝反复失败、根因不明、范围持续发散或出现 flake 时,应要求先做最小化、
54
- 证据驱动的诊断。**一轮轮只加不减的修复流,本身就是该上报的病。**
42
+ 删除或简化根因优先于增加平行机制。提出或批准护栏前必答三问:①哪个真实、
43
+ 可复现的失败证明需要它?②哪个接缝拥有它要维护的不变式?③为什么删除或简化
44
+ 根因不足以解决这类失败?
55
45
 
56
46
  审查修复波及面,区分原始缺陷所需改动、修复引入后再修的改动和无依据的新机制;
57
- 后两类主导时,优先删除/简化。超出**本次授权**的取舍必须上抛。
47
+ 后两类主导时优先删除/简化。超出本次授权的取舍必须上抛。**一轮轮只加不减的
48
+ 修复流,本身就是该上报的病。**
58
49
 
59
50
  ## 判词
60
51
 
61
52
  合法判词只有三种,含义相对所审材料的举证责任:
62
53
 
63
54
  - `converged` — 适用举证责任下的事项均已明确处置。修内司/将作监的提交另需:
64
- 本轮御史台无 p2 及以上 finding(p 级由你自行判定;被你驳回的 finding 不计数)。
55
+ 本轮御史台无 p2 及以上 finding(p 级由你自行判定;被你驳回的不计数)。
65
56
  - `continue` — 仍有可施工的问题;给出清晰、可验证的修理正文。
66
57
  - `escalate` — 存在必须由陛下决定的门槛、相互冲突的法源,或依据失败走势
67
- 与专业证据已无法形成安全裁定;写明问题**和可选项**。
58
+ 与专业证据已无法形成安全裁定;写明问题和可选项。
68
59
 
69
60
  **送修正文由你执笔——判词即包。** 首轮可薄(finding + 法源锚点 + 边界);有
70
- 病史则综合(病史全列、方向可钉、拆除清单明文)。判其发散(同缝反复、根因
71
- 不明、疑跨接缝、flake)时,在送修单中提示先做诊断定根因。
61
+ 病史则综合(病史全列、方向可钉、拆除清单明文)。同缝反复、根因不明、范围
62
+ 持续发散、疑跨接缝或 flake 即为发散,送修单中要求先做最小化、证据驱动的
63
+ 诊断定根因。
72
64
 
73
65
  `continue` 的 `classes` 只含尚未结清、须派施工的根因类;已结清项、过程账、
74
66
  双向抽样不得进 classes。
@@ -1,12 +1,13 @@
1
1
  /**
2
- * Package-owned engine detour tool (#357 T2).
3
- * Registered by shared role-runtime when Judge + engine activation signal is present.
4
- * Forbidden in judge-role.ts (lifecycle ban — no spawn in role modules).
2
+ * Package-owned engine detour tool (#357 T2 / #378).
3
+ * Registered by shared role-runtime when Judge|Reviewer + engine activation signal is present.
4
+ * Evidence-child legs install the same definition via customTools (no spawn in role modules).
5
5
  */
6
6
  import type {
7
7
  AgentToolResult,
8
8
  ExtensionAPI,
9
9
  ExtensionContext,
10
+ ToolDefinition,
10
11
  } from "@earendil-works/pi-coding-agent";
11
12
  import { Type, type Static } from "typebox";
12
13
 
@@ -18,6 +19,7 @@ import {
18
19
  isEngineDetourFailure,
19
20
  runEngineDetourOnce,
20
21
  } from "./engine-detour.ts";
22
+ import { wrapPackageOwnedToolDefinition } from "./package-owned-tool-idle.ts";
21
23
 
22
24
  const engineDetourArgsSchema = Type.Object(
23
25
  {
@@ -45,28 +47,21 @@ export type EngineDetourToolRegistration = {
45
47
  readonly registered: boolean;
46
48
  };
47
49
 
50
+ type EngineDetourLatch = { used: boolean };
51
+
48
52
  /**
49
- * Register the engine-generic detour tool once for this process when Judge has
50
- * an engine activation signal. Returns whether registration occurred.
51
- * Once-latch is activation-scoped via the returned reset handle.
53
+ * Build one once-latch detour tool definition for a configured engine name.
54
+ * `latch` is shared so parent registration can reset between activations.
55
+ * `fail` owns host abort (parent) vs throw (evidence child).
52
56
  */
53
- export function registerEngineDetourTool(
54
- pi: ExtensionAPI,
55
- hostActions: EngineDetourHostActions,
56
- ): EngineDetourToolRegistration & { resetLatch(): void } {
57
- const engineName = engineNameFromEnv();
58
- if (engineName === undefined) {
59
- return {
60
- registered: false,
61
- resetLatch() {
62
- /* no-op when unregistered */
63
- },
64
- };
65
- }
66
-
67
- let used = false;
68
-
69
- pi.registerTool({
57
+ export function createEngineDetourToolDefinition(input: {
58
+ engineName: string;
59
+ latch?: EngineDetourLatch;
60
+ fail: (error: Error, toolCallId: string, ctx: ExtensionContext) => never;
61
+ }): ToolDefinition {
62
+ const latch = input.latch ?? { used: false };
63
+ const engineName = input.engineName;
64
+ return wrapPackageOwnedToolDefinition({
70
65
  name: ENGINE_DETOUR_TOOL_NAME,
71
66
  label: "Engine Detour",
72
67
  description:
@@ -75,7 +70,7 @@ export function registerEngineDetourTool(
75
70
  promptGuidelines: [
76
71
  `Use ${ENGINE_DETOUR_TOOL_NAME} exactly once for the configured engine (${engineName}). Optional packaged notes are guidance when present; a bare engine name alone is also a valid call path.`,
77
72
  "Pass argv for the host CLI of this engine name — first element is the executable name on PATH. Follow optional packaged notes when delivered; otherwise act from the engine name and the host CLI actual interface. Do not invent package flags.",
78
- "On success, use the returned stdout as labor content for the existing typed submission tool.",
73
+ "On success, use the returned stdout as labor content for the existing typed submission / report path.",
79
74
  ],
80
75
  parameters: engineDetourArgsSchema,
81
76
  async execute(
@@ -85,22 +80,22 @@ export function registerEngineDetourTool(
85
80
  _onUpdate,
86
81
  ctx,
87
82
  ): Promise<AgentToolResult<unknown>> {
88
- if (used) {
89
- hostActions.failInfrastructure(
83
+ if (latch.used) {
84
+ input.fail(
90
85
  new Error(ENGINE_DETOUR_ALREADY_USED_DIAGNOSTIC),
91
- ctx,
92
86
  toolCallId,
87
+ ctx,
93
88
  );
94
89
  }
95
- used = true;
90
+ latch.used = true;
96
91
 
97
92
  const args = params as EngineDetourArgs;
98
93
  const argv = Array.isArray(args.argv) ? args.argv : [];
99
94
  if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
100
- hostActions.failInfrastructure(
95
+ input.fail(
101
96
  new Error("engine detour argv must be a non-empty string array"),
102
- ctx,
103
97
  toolCallId,
98
+ ctx,
104
99
  );
105
100
  }
106
101
 
@@ -112,14 +107,18 @@ export function registerEngineDetourTool(
112
107
  ...(signal === undefined ? {} : { signal }),
113
108
  });
114
109
  } catch (error) {
115
- hostActions.failInfrastructure(error, ctx, toolCallId);
110
+ input.fail(
111
+ error instanceof Error ? error : new Error(String(error)),
112
+ toolCallId,
113
+ ctx,
114
+ );
116
115
  }
117
116
 
118
117
  if (isEngineDetourFailure(result)) {
119
- hostActions.failInfrastructure(
118
+ input.fail(
120
119
  new Error(engineDetourFailureDiagnostic(result)),
121
- ctx,
122
120
  toolCallId,
121
+ ctx,
123
122
  );
124
123
  }
125
124
 
@@ -131,12 +130,42 @@ export function registerEngineDetourTool(
131
130
  },
132
131
  };
133
132
  },
133
+ }) as ToolDefinition;
134
+ }
135
+
136
+ /**
137
+ * Register the engine-generic detour tool once for this process when Judge/Reviewer has
138
+ * an engine activation signal. Returns whether registration occurred.
139
+ * Once-latch is activation-scoped via the returned reset handle.
140
+ */
141
+ export function registerEngineDetourTool(
142
+ pi: ExtensionAPI,
143
+ hostActions: EngineDetourHostActions,
144
+ ): EngineDetourToolRegistration & { resetLatch(): void } {
145
+ const engineName = engineNameFromEnv();
146
+ if (engineName === undefined) {
147
+ return {
148
+ registered: false,
149
+ resetLatch() {
150
+ /* no-op when unregistered */
151
+ },
152
+ };
153
+ }
154
+
155
+ const latch: EngineDetourLatch = { used: false };
156
+ const definition = createEngineDetourToolDefinition({
157
+ engineName,
158
+ latch,
159
+ fail(error, toolCallId, ctx) {
160
+ hostActions.failInfrastructure(error, ctx, toolCallId);
161
+ },
134
162
  });
163
+ pi.registerTool(definition);
135
164
 
136
165
  return {
137
166
  registered: true,
138
167
  resetLatch() {
139
- used = false;
168
+ latch.used = false;
140
169
  },
141
170
  };
142
171
  }