@akagilnc/pi-workflow-roles 0.1.2525 → 0.1.2537

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @akagilnc/pi-workflow-roles
2
2
 
3
- Packaged workflow roles for [Pi](https://pi.dev): `judge`, `fixer`, `coder`, `reviewer`, `collector`, `doctor`, `merger`, `notary`, `analyst`. 中文说明见 [README.zh-CN.md](https://github.com/Akagilnc/ak-pi-workflow-roles/blob/main/README.zh-CN.md)。
3
+ Packaged workflow roles for [Pi](https://pi.dev): `judge`, `fixer`, `coder`, `reviewer`, `collector`, `doctor`, `merger`, `notary`, `inspector`, `analyst`. 中文说明见 [README.zh-CN.md](https://github.com/Akagilnc/ak-pi-workflow-roles/blob/main/README.zh-CN.md)。
4
4
 
5
5
  ## Install
6
6
 
@@ -48,7 +48,7 @@ Seat and Gate-officer configuration:
48
48
  ```bash
49
49
  ak-role config set judge <provider/model[:thinking]>
50
50
  ak-role config set navigator <provider/model[:thinking]>
51
- # Gate officers (automatic on submission; not caller commands except direct notary)
51
+ # Gate officers (automatic on submission; inspector and notary also have direct commands)
52
52
  ak-role config set gatekeeper <provider/model[:thinking]>
53
53
  ak-role config set inspector <provider/model[:thinking]>
54
54
  ak-role config set notary <provider/model[:thinking]>
@@ -95,6 +95,9 @@ ak-role merger --project /path/to/worktree "Reconcile the active merge."
95
95
  # notary — document-fidelity check on one retained source run; one-shot
96
96
  ak-role notary --source-run <runId@role|path>
97
97
 
98
+ # inspector — direct complexity and test-quality check
99
+ ak-role inspector --attach ./change.patch "Review this material."
100
+
98
101
  # analyst — deterministic metrics; bare call = whole book
99
102
  ak-role analyst
100
103
 
@@ -0,0 +1 @@
1
+ export const INSPECTOR_OUTPUT_TOOL_NAME = "ak_inspector_output";
@@ -6,6 +6,7 @@ import { COLLECTOR_ACCEPTED_TEXT, COLLECTOR_OUTPUT_TOOL, validateAcceptedCollect
6
6
  import { JUDGE_ACCEPTED_TEXT, JUDGE_OUTPUT_TOOL_NAME, validateAcceptedJudgeDetails, } from "./judge-output.js";
7
7
  import { REVIEWER_ACCEPTED_TEXT, REVIEWER_OUTPUT_TOOL_NAME, projectReviewerIntentToReceipt, validateReviewerIntent, validateRuntimeReviewerReceipt, } from "./reviewer-output.js";
8
8
  import { isAuditEscalationResult } from "../audit-escalation.js";
9
+ import { INSPECTOR_OUTPUT_TOOL_NAME } from "../inspector-contracts.js";
9
10
  import { DOCTOR_ACCEPTED_TEXT, DOCTOR_OUTPUT_TOOL_NAME, validateDoctorSubmissionShape, validateRecordedDoctorOutput } from "../doctor-contracts.js";
10
11
  import { MERGER_ACCEPTED_TEXT, MERGER_OUTPUT_TOOL_NAME, validateMergerOutput } from "../merger-contracts.js";
11
12
  import { NOTARY_ACCEPTED_TEXT, NOTARY_OUTPUT_TOOL_NAME, validateRecordedNotaryOutput } from "../notary-contracts.js";
@@ -20,6 +21,7 @@ export const TERMINATING_TOOL_NAMES = [
20
21
  DOCTOR_OUTPUT_TOOL_NAME,
21
22
  MERGER_OUTPUT_TOOL_NAME,
22
23
  NOTARY_OUTPUT_TOOL_NAME,
24
+ INSPECTOR_OUTPUT_TOOL_NAME,
23
25
  ];
24
26
  export function isTerminatingToolName(name) {
25
27
  return TERMINATING_TOOL_NAMES.includes(name);
@@ -42,6 +44,8 @@ export function acceptedTextFor(toolName) {
42
44
  return MERGER_ACCEPTED_TEXT;
43
45
  case NOTARY_OUTPUT_TOOL_NAME:
44
46
  return NOTARY_ACCEPTED_TEXT;
47
+ case INSPECTOR_OUTPUT_TOOL_NAME:
48
+ return "给事中回执已受理";
45
49
  }
46
50
  }
47
51
  export class AcceptedDetailsContractError extends Error {
@@ -82,6 +86,7 @@ export function validateAcceptedDetails(toolName, details) {
82
86
  [DOCTOR_OUTPUT_TOOL_NAME]: ["completed", "refused"],
83
87
  [MERGER_OUTPUT_TOOL_NAME]: ["completed", "escalate"],
84
88
  [NOTARY_OUTPUT_TOOL_NAME]: ["pass", "bounce"],
89
+ [INSPECTOR_OUTPUT_TOOL_NAME]: ["pass", "bounce"],
85
90
  };
86
91
  const collectorDiscriminator = toolName === COLLECTOR_OUTPUT_TOOL && Array.isArray(candidate?.groups);
87
92
  const baseDiscriminator = discriminator;
@@ -109,6 +114,8 @@ export function validateAcceptedDetails(toolName, details) {
109
114
  return validateMergerOutput(details);
110
115
  case NOTARY_OUTPUT_TOOL_NAME:
111
116
  return validateRecordedNotaryOutput(details);
117
+ case INSPECTOR_OUTPUT_TOOL_NAME:
118
+ return candidate;
112
119
  }
113
120
  }
114
121
  catch (error) {
@@ -146,7 +153,8 @@ export function acceptedFacts(toolName, details) {
146
153
  case FIXER_OUTPUT_TOOL_NAME:
147
154
  case REVIEWER_OUTPUT_TOOL_NAME:
148
155
  case DOCTOR_OUTPUT_TOOL_NAME:
149
- case NOTARY_OUTPUT_TOOL_NAME: return { status: details.status };
156
+ case NOTARY_OUTPUT_TOOL_NAME:
157
+ case INSPECTOR_OUTPUT_TOOL_NAME: return { status: details.status };
150
158
  case JUDGE_OUTPUT_TOOL_NAME: return { status: details.judgeStatus };
151
159
  case MERGER_OUTPUT_TOOL_NAME: {
152
160
  const output = details;
@@ -5,6 +5,7 @@ import { CODER_OUTPUT_TOOL_NAME, FIXER_OUTPUT_TOOL_NAME } from "./package-contra
5
5
  import { DOCTOR_OUTPUT_TOOL_NAME } from "./doctor-contracts.js";
6
6
  import { MERGER_OUTPUT_TOOL_NAME } from "./merger-contracts.js";
7
7
  import { NOTARY_OUTPUT_TOOL_NAME } from "./notary-contracts.js";
8
+ import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.js";
8
9
  const PACKAGED_ROLE_REGISTRY = [
9
10
  { role: "judge", phases: [null], outputTool: JUDGE_OUTPUT_TOOL_NAME, inputFlag: void 0, phaseFlag: void 0, activationStage: "load-and-install" },
10
11
  { role: "fixer", phases: ["plan", "apply"], outputTool: FIXER_OUTPUT_TOOL_NAME, inputFlag: "ak-fix-packet", phaseFlag: "ak-fixer-phase", activationStage: "load-and-install" },
@@ -14,7 +15,8 @@ const PACKAGED_ROLE_REGISTRY = [
14
15
  { role: "collector", phases: [null], outputTool: COLLECTOR_OUTPUT_TOOL, inputFlag: void 0, phaseFlag: void 0, activationStage: "load-and-install" },
15
16
  { role: "doctor", phases: [null], outputTool: DOCTOR_OUTPUT_TOOL_NAME, inputFlag: "ak-doctor-case", phaseFlag: void 0, activationStage: "load-and-install" },
16
17
  { role: "merger", phases: [null], outputTool: MERGER_OUTPUT_TOOL_NAME, inputFlag: "ak-merger-input", phaseFlag: void 0, activationStage: "prepare-git-and-install" },
17
- { role: "notary", phases: [null], outputTool: NOTARY_OUTPUT_TOOL_NAME, inputFlag: "ak-notary-source-run", phaseFlag: void 0, activationStage: "load-and-install" }
18
+ { role: "notary", phases: [null], outputTool: NOTARY_OUTPUT_TOOL_NAME, inputFlag: "ak-notary-source-run", phaseFlag: void 0, activationStage: "load-and-install" },
19
+ { role: "inspector", phases: [null], outputTool: INSPECTOR_OUTPUT_TOOL_NAME, inputFlag: void 0, phaseFlag: void 0, activationStage: "load-and-install" }
18
20
  ];
19
21
  function packagedRoleMetadata(role) {
20
22
  return PACKAGED_ROLE_REGISTRY.find((entry) => entry.role === role);
@@ -67,21 +67,21 @@ export function setPersistentSeatConfig(config, seat, selection) {
67
67
  /**
68
68
  * Clear a gate officer's persistent model override (#453).
69
69
  * Scope is GateOfficerSeat only — non-province seats have no destructive clear seam.
70
- * Only notary may retain an engine-only residual so direct notary activation keeps
70
+ * Directly callable gate officers may retain an engine-only residual so activation keeps
71
71
  * its labor engine while model resolution returns to startup / province inheritance.
72
- * gatekeeper/inspector drop the whole row. Already-absent seats are a no-op.
72
+ * Gatekeeper drops the whole row. Already-absent seats are a no-op.
73
73
  */
74
74
  export function clearPersistentSeatConfig(config, seat) {
75
75
  const previous = config.seats[seat];
76
76
  if (previous === undefined)
77
77
  return config;
78
- // Engine-only residual ownership is notary-only — never widen to other officers.
79
- if (seat === "notary" && previous.engine !== undefined) {
78
+ // Direct officer engine ownership survives an independent model clear.
79
+ if ((seat === "notary" || seat === "inspector") && previous.engine !== undefined) {
80
80
  return {
81
81
  ...config,
82
82
  seats: {
83
83
  ...config.seats,
84
- notary: { engine: previous.engine },
84
+ [seat]: { engine: previous.engine },
85
85
  },
86
86
  };
87
87
  }
@@ -111,8 +111,8 @@ export function isEngineAxisSeat(seat) {
111
111
  }
112
112
  /**
113
113
  * Set or clear persistent engine on a callable role seat (#356 / #378 / #391 / #453).
114
- * First engine still requires an existing seat row (model, or notary engine residual).
115
- * Clearing engine from a notary engine-only residual drops the empty row; clearing
114
+ * First engine still requires an existing seat row (model, or direct-officer engine residual).
115
+ * Clearing engine from an engine-only residual drops the empty row; clearing
116
116
  * engine from a model+engine row leaves model-only. Seat type is PublicCallableRole
117
117
  * (navigator excluded at the type boundary).
118
118
  */
@@ -297,10 +297,10 @@ function parseSeatModelConfig(value, seat) {
297
297
  // validatePublicCliConfigEngines → assertLegalEngineName (single authority).
298
298
  throw new Error(`config seat ${seat} engine must be a string`);
299
299
  }
300
- // #453: engine-only residual is legal only for notary after model clear.
300
+ // Direct officer engine-only residual remains legal after model clear.
301
301
  // All other seats keep the baseline provider/model required contract.
302
302
  if (!hasProvider) {
303
- if (seat === "notary" && typeof raw.engine === "string") {
303
+ if ((seat === "notary" || seat === "inspector") && typeof raw.engine === "string") {
304
304
  if (raw.thinking !== undefined) {
305
305
  throw new Error(`config seat ${seat} thinking requires provider/model`);
306
306
  }