@akagilnc/pi-workflow-roles 0.1.1918 → 0.1.2004
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/evidence-child-executor.js +12 -6
- package/dist/package-contracts/reviewer-output.js +16 -0
- package/dist/public-cli/main.js +2520 -72
- package/dist/reviewer-construction.js +46 -6
- package/dist/reviewer-dispatch.js +64 -1
- package/dist/reviewer-execution-ledger.js +2 -1
- package/dist/reviewer-pinned-git.js +33 -0
- package/package.json +1 -1
- package/src/atomic-write.ts +26 -0
- package/src/evidence-child-executor.ts +13 -6
- package/src/ledger-session-read.ts +260 -0
- package/src/package-contracts/reviewer-output.ts +19 -0
- package/src/public-cli/cli.ts +24 -0
- package/src/public-cli/invocation.ts +398 -2
- package/src/public-cli/main.ts +21 -2
- package/src/public-cli/registry.ts +18 -1
- package/src/public-cli/reviewer-run.ts +12 -0
- package/src/public-cli/run-lifecycle.ts +22 -1
- package/src/public-cli/settlement.ts +17 -0
- package/src/public-cli/taishi-run.ts +235 -0
- package/src/reviewer-construction.ts +64 -7
- package/src/reviewer-dispatch.ts +81 -2
- package/src/reviewer-execution-ledger.ts +2 -1
- package/src/reviewer-pinned-git.ts +43 -0
- package/src/reviewer-role.ts +143 -132
- package/src/reviewer-settlement.ts +4 -0
- package/src/role-runtime.ts +186 -3
- package/src/run-terminal-artifacts.ts +231 -0
- package/src/taishi-cohort.ts +232 -0
- package/src/taishi-entry.ts +429 -0
- package/src/taishi-index.ts +269 -0
- package/src/taishi-ledger.ts +466 -0
- package/src/taishi-median.ts +15 -0
- package/src/taishi-metric-families/acceptance-success-rework.ts +346 -0
- package/src/taishi-metric-families/b2-frame-buckets-actions.ts +274 -0
- package/src/taishi-metric-families/leg-wall-clock.ts +90 -0
- package/src/taishi-metric-families/round-timeline.ts +201 -0
- package/src/taishi-metric-families.ts +36 -0
- package/src/taishi-metric-family.ts +41 -0
- package/src/taishi-model-groups.ts +198 -0
- package/src/taishi-page.ts +320 -0
- package/src/ticket-trajectory.ts +9 -62
package/src/reviewer-role.ts
CHANGED
|
@@ -3,10 +3,12 @@ import { Type } from "typebox";
|
|
|
3
3
|
import { openToolObjectFromUnion } from "./open-tool-schema.ts";
|
|
4
4
|
|
|
5
5
|
import type { AnyCanonicalSkillBinding, CanonicalSkillBinding } from "./canonical-skill-binding.ts";
|
|
6
|
+
export type { CanonicalSkillBinding };
|
|
6
7
|
import { disposeComplianceDecision } from "./audit-escalation.ts";
|
|
7
8
|
import type { ComplianceDecision } from "./compliance-transport.ts";
|
|
8
9
|
import { appendActiveSessionCustomEntry } from "./compliance-transport.ts";
|
|
9
10
|
import { REVIEWER_CANDIDATE_ENTRY_TYPE } from "./dossier-resolution.ts";
|
|
11
|
+
import { type ReviewerSpecDisposition } from "./reviewer-construction.ts";
|
|
10
12
|
import { createReviewerDispatcher, type AcceptedReviewerDispatch, type AcceptedReviewerExecution, type ReviewerPinnedGitReader } from "./reviewer-dispatch.ts";
|
|
11
13
|
import { ReviewerDispatchExecutionError, type ReviewerDispatchRunResult } from "./reviewer-agent.ts";
|
|
12
14
|
import { createReviewerExecutionLedger, projectAcceptedDispatch, projectReviewerDispatchOutcome, type ReviewerExecutionRecord } from "./reviewer-execution-ledger.ts";
|
|
@@ -17,6 +19,13 @@ export { REVIEWER_OUTPUT_TOOL_NAME };
|
|
|
17
19
|
export type { ReviewerIntent };
|
|
18
20
|
export const AGENT_TOOL_NAME = "Agent";
|
|
19
21
|
|
|
22
|
+
/** Frozen admitted inputs the behavior layer may consume — no flag surface. */
|
|
23
|
+
export type ReviewerAdmittedInputs = Readonly<{
|
|
24
|
+
baseRevision: string;
|
|
25
|
+
reviewScopeKeys?: readonly string[];
|
|
26
|
+
authorityRefs?: readonly string[];
|
|
27
|
+
}>;
|
|
28
|
+
|
|
20
29
|
const reviewerOutputVariants = Type.Union([
|
|
21
30
|
Type.Object({ status: Type.Literal("completed", { description: "Reviewer dispatch completed." }) }, { additionalProperties: false }),
|
|
22
31
|
Type.Object({ status: Type.Literal("refused", { description: "Reviewer dispatch was lawfully refused." }), diagnostic: Type.String({ minLength: 1, description: "Diagnostic explaining the refusal." }) }, { additionalProperties: false }),
|
|
@@ -42,157 +51,159 @@ function requireSoleReviewerOutputCall(id: string, ctx: ExtensionContext): void
|
|
|
42
51
|
export type ReviewerActivation = Readonly<{
|
|
43
52
|
dispatcher: ReturnType<typeof createReviewerDispatcher>;
|
|
44
53
|
fixedBaseRevision: string;
|
|
54
|
+
soul: string;
|
|
55
|
+
/** Frozen code-review binding — envelope owns expansion capture against this data. */
|
|
56
|
+
skillBinding: CanonicalSkillBinding<"code-review">;
|
|
57
|
+
/** Honest Spec disposition after accepted dispatch, for envelope parent prompt assembly. */
|
|
58
|
+
getSpecDisposition(): ReviewerSpecDisposition | undefined;
|
|
45
59
|
}>;
|
|
46
60
|
|
|
47
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Reviewer behavior runtime: label, soul, evidence tools, decision tool, projection.
|
|
63
|
+
* No flag registration/decoding and no agent_start prompt lifecycle (ADR 0018 / envelope).
|
|
64
|
+
*/
|
|
65
|
+
export function createReviewerRoleRuntime(
|
|
66
|
+
pi: ExtensionAPI,
|
|
67
|
+
dependencies: ReviewerRoleDependencies,
|
|
68
|
+
hostActions: ReviewerRoleHostActions,
|
|
69
|
+
): {
|
|
70
|
+
activate(ctx: ExtensionContext | undefined, admitted: ReviewerAdmittedInputs): Promise<ReviewerActivation>;
|
|
71
|
+
} {
|
|
48
72
|
let soul: string | undefined;
|
|
49
73
|
let binding: CanonicalSkillBinding<"code-review"> | undefined;
|
|
50
74
|
let reader: ReviewerPinnedGitReader | undefined;
|
|
51
75
|
let dispatcher: ReturnType<typeof createReviewerDispatcher> | undefined;
|
|
52
|
-
let originalRequest: string | undefined;
|
|
53
|
-
let expansionCaptured = false;
|
|
54
76
|
let registered = false;
|
|
55
|
-
let reviewScopeKeys: readonly string[] | undefined;
|
|
56
77
|
let fixedBaseRevision: string | undefined;
|
|
78
|
+
let acceptedDispatch: AcceptedReviewerDispatch | undefined;
|
|
57
79
|
const ledger = createReviewerExecutionLedger();
|
|
58
|
-
pi.registerFlag("ak-review-base", { description: "Fixed base revision for the pinned review target", type: "string" });
|
|
59
|
-
pi.registerFlag("ak-review-scope-keys", { description: "Optional comma-separated exact class keys limiting Reviewer scope", type: "string" });
|
|
60
80
|
|
|
61
|
-
return {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
reviewScopeKeys = parsed;
|
|
75
|
-
}
|
|
76
|
-
const baseRevision = pi.getFlag("ak-review-base");
|
|
77
|
-
if (typeof baseRevision !== "string" || !baseRevision.trim()) throw new Error("Reviewer role requires --ak-review-base");
|
|
78
|
-
fixedBaseRevision = baseRevision;
|
|
79
|
-
const loaded = await dependencies.loadCanonicalSkillBinding("code-review");
|
|
80
|
-
if (loaded.name !== "code-review") throw new Error("Canonical Skill binding loader returned tdd for code-review");
|
|
81
|
-
binding = loaded;
|
|
82
|
-
reader = await dependencies.createPinnedGitReader();
|
|
81
|
+
return {
|
|
82
|
+
async activate(_ctx, admitted) {
|
|
83
|
+
soul = (await dependencies.loadSoul()).trim();
|
|
84
|
+
if (!soul) throw new Error("Reviewer soul is empty");
|
|
85
|
+
// Behavior layer receives frozen admitted inputs only — no pi.getFlag.
|
|
86
|
+
fixedBaseRevision = admitted.baseRevision;
|
|
87
|
+
const reviewScopeKeys = admitted.reviewScopeKeys;
|
|
88
|
+
const authorityRefs = admitted.authorityRefs;
|
|
89
|
+
const loaded = await dependencies.loadCanonicalSkillBinding("code-review");
|
|
90
|
+
if (loaded.name !== "code-review") throw new Error("Canonical Skill binding loader returned tdd for code-review");
|
|
91
|
+
binding = loaded;
|
|
92
|
+
reader = await dependencies.createPinnedGitReader();
|
|
83
93
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
const result = await dependencies.runDispatch(execution, { context, ...(signal === undefined ? {} : { signal }) });
|
|
92
|
-
projectReviewerDispatchOutcome(ledger, dispatch, result);
|
|
93
|
-
return result;
|
|
94
|
-
} catch (error) {
|
|
95
|
-
if (error instanceof ReviewerDispatchExecutionError) {
|
|
96
|
-
try { projectReviewerDispatchOutcome(ledger, dispatch, error.outcome); }
|
|
97
|
-
catch (mismatch) { throw ledger.recordInfrastructureFailure(mismatch); }
|
|
98
|
-
throw error;
|
|
99
|
-
}
|
|
100
|
-
throw ledger.recordInfrastructureFailure(error);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
dispatcher = createReviewerDispatcher({
|
|
104
|
-
canonicalSkill: binding.snapshot.raw,
|
|
105
|
-
reader,
|
|
106
|
-
...(reviewScopeKeys === undefined ? {} : { reviewScopeKeys }),
|
|
107
|
-
decisionEvidence(decision) {
|
|
94
|
+
acceptedDispatch = undefined;
|
|
95
|
+
const executeAndProjectDispatch = async (execution: AcceptedReviewerExecution, invocation: unknown): Promise<ReviewerDispatchRunResult> => {
|
|
96
|
+
const dispatch = acceptedDispatch;
|
|
97
|
+
if (dispatch === undefined || dispatch.identity !== execution.identity) throw new Error("Reviewer execution lacks accepted construction evidence");
|
|
98
|
+
const { context, signal } = invocation as { context: ExtensionContext; signal?: AbortSignal };
|
|
99
|
+
ledger.append({ source: "reviewer-agent", type: "dispatch-started", dispatchIdentity: execution.identity, cardinality: execution.legs.length as 1 | 2 });
|
|
108
100
|
try {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
} else ledger.append({ source: "reviewer-dispatch", type: "rejected", identity: decision.identity, violations: decision.violations, started: false });
|
|
101
|
+
const result = await dependencies.runDispatch(execution, { context, ...(signal === undefined ? {} : { signal }) });
|
|
102
|
+
projectReviewerDispatchOutcome(ledger, dispatch, result);
|
|
103
|
+
return result;
|
|
113
104
|
} catch (error) {
|
|
105
|
+
if (error instanceof ReviewerDispatchExecutionError) {
|
|
106
|
+
try { projectReviewerDispatchOutcome(ledger, dispatch, error.outcome); }
|
|
107
|
+
catch (mismatch) { throw ledger.recordInfrastructureFailure(mismatch); }
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
114
110
|
throw ledger.recordInfrastructureFailure(error);
|
|
115
111
|
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
"This runtime executes the Standards and Spec review legs for you as package-managed evidence-child sessions — that IS this runtime's implementation of the review Skill's parallel sub-agents. Do not refuse because no Agent tool appears in your tool list, and do not substitute your own sub-processes; work with the legs the runtime provides. The same rule applies to corrections and redos after an auditor bounce-back: complete them in this session with your own tools. Evidence-leg model and thinking tier follow the seat's active order; the reviewer seat does not choose them."], parameters: reviewerOutputSchema,
|
|
124
|
-
async execute(id, parameters, signal, _update, toolCtx): Promise<AgentToolResult<unknown>> {
|
|
125
|
-
if (!soul || !binding) throw new Error("Reviewer inputs were not loaded");
|
|
126
|
-
requireSoleReviewerOutputCall(id, toolCtx);
|
|
127
|
-
const output = validateReviewerIntent(parameters);
|
|
128
|
-
if (output.status === "completed" && !expansionCaptured) throw new Error("Reviewer completed requires canonical Skill expansion capture");
|
|
129
|
-
let record: ReviewerExecutionRecord;
|
|
130
|
-
try { record = ledger.recordForAudit(output.status); } catch (error) { if ((error as any)?.fatalReviewerInfrastructure) hostActions.failInfrastructure(error, toolCtx, id); throw error; }
|
|
131
|
-
const candidate = assembleRuntimeReviewerReceipt({
|
|
132
|
-
intent: output,
|
|
133
|
-
record,
|
|
134
|
-
canonicalSkillText: binding.snapshot.raw,
|
|
135
|
-
});
|
|
136
|
-
// First-record-then-audit: candidate lands on the parent session books
|
|
137
|
-
// before the auditor is spawned (zero hand-delivery).
|
|
112
|
+
};
|
|
113
|
+
dispatcher = createReviewerDispatcher({
|
|
114
|
+
canonicalSkill: binding.snapshot.raw,
|
|
115
|
+
reader,
|
|
116
|
+
...(reviewScopeKeys === undefined ? {} : { reviewScopeKeys }),
|
|
117
|
+
...(authorityRefs === undefined ? {} : { authorityRefs }),
|
|
118
|
+
decisionEvidence(decision) {
|
|
138
119
|
try {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
{
|
|
144
|
-
unavailable: "reviewer candidate retention is unavailable",
|
|
145
|
-
failed: "reviewer candidate retention failed",
|
|
146
|
-
},
|
|
147
|
-
);
|
|
120
|
+
if (decision.disposition === "accepted") {
|
|
121
|
+
ledger.append(projectAcceptedDispatch(decision.dispatch));
|
|
122
|
+
acceptedDispatch = decision.dispatch;
|
|
123
|
+
} else ledger.append({ source: "reviewer-dispatch", type: "rejected", identity: decision.identity, violations: decision.violations, started: false });
|
|
148
124
|
} catch (error) {
|
|
149
|
-
|
|
125
|
+
throw ledger.recordInfrastructureFailure(error);
|
|
150
126
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
127
|
+
},
|
|
128
|
+
run: executeAndProjectDispatch,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (!registered) {
|
|
132
|
+
registered = true;
|
|
133
|
+
pi.registerTool({ name: REVIEWER_OUTPUT_TOOL_NAME, label: "Reviewer Output", description: "Submit the thin Reviewer receipt after semantic compliance audit.", promptSnippet: "Submit the final Reviewer receipt", promptGuidelines: [`Use ${REVIEWER_OUTPUT_TOOL_NAME} as the sole final action.`,
|
|
134
|
+
"This runtime executes the Standards and Spec review legs for you as package-managed evidence-child sessions — that IS this runtime's implementation of the review Skill's parallel sub-agents. Do not refuse because no Agent tool appears in your tool list, and do not substitute your own sub-processes; work with the legs the runtime provides. The same rule applies to corrections and redos after an auditor bounce-back: complete them in this session with your own tools. Evidence-leg model and thinking tier follow the seat's active order; the reviewer seat does not choose them."], parameters: reviewerOutputSchema,
|
|
135
|
+
async execute(id, parameters, signal, _update, toolCtx): Promise<AgentToolResult<unknown>> {
|
|
136
|
+
if (!soul || !binding) throw new Error("Reviewer inputs were not loaded");
|
|
137
|
+
requireSoleReviewerOutputCall(id, toolCtx);
|
|
138
|
+
const output = validateReviewerIntent(parameters);
|
|
139
|
+
let record: ReviewerExecutionRecord;
|
|
140
|
+
try { record = ledger.recordForAudit(output.status); } catch (error) { if ((error as any)?.fatalReviewerInfrastructure) hostActions.failInfrastructure(error, toolCtx, id); throw error; }
|
|
141
|
+
const candidate = assembleRuntimeReviewerReceipt({
|
|
142
|
+
intent: output,
|
|
143
|
+
record,
|
|
144
|
+
canonicalSkillText: binding.snapshot.raw,
|
|
156
145
|
});
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
146
|
+
// First-record-then-audit: candidate lands on the parent session books
|
|
147
|
+
// before the auditor is spawned (zero hand-delivery).
|
|
148
|
+
try {
|
|
149
|
+
appendActiveSessionCustomEntry(
|
|
150
|
+
toolCtx,
|
|
151
|
+
REVIEWER_CANDIDATE_ENTRY_TYPE,
|
|
152
|
+
{ version: 1, candidate },
|
|
153
|
+
{
|
|
154
|
+
unavailable: "reviewer candidate retention is unavailable",
|
|
155
|
+
failed: "reviewer candidate retention failed",
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
hostActions.failInfrastructure(ledger.recordInfrastructureFailure(error), toolCtx, id);
|
|
160
|
+
}
|
|
161
|
+
let audit: ComplianceDecision;
|
|
162
|
+
try {
|
|
163
|
+
audit = await dependencies.auditCompliance({
|
|
164
|
+
context: toolCtx,
|
|
165
|
+
...(signal === undefined ? {} : { signal }),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
catch (error) { hostActions.failInfrastructure(ledger.recordInfrastructureFailure(error), toolCtx, id); }
|
|
169
|
+
return disposeComplianceDecision<AgentToolResult<unknown>>(
|
|
170
|
+
audit,
|
|
171
|
+
{
|
|
172
|
+
pass: async (usage) => {
|
|
173
|
+
try { await dependencies.shutdownAgent?.(); } catch (error) { hostActions.failInfrastructure(ledger.recordInfrastructureFailure(error), toolCtx, id); }
|
|
174
|
+
return { content: [{ type: "text" as const, text: "Reviewer report accepted" }], details: candidate, terminate: true as const, ...(usage === undefined ? {} : { usage }) };
|
|
175
|
+
},
|
|
176
|
+
noReceipt: async (auditNoReceipt, usageProjection) => {
|
|
177
|
+
try { await dependencies.shutdownAgent?.(); } catch (error) { hostActions.failInfrastructure(ledger.recordInfrastructureFailure(error), toolCtx, id); }
|
|
178
|
+
return { content: [{ type: "text" as const, text: "Reviewer report accepted; compliance audit produced no receipt" }], details: { ...candidate, auditNoReceipt }, terminate: true as const, ...usageProjection };
|
|
179
|
+
},
|
|
180
|
+
revise: (violations) => {
|
|
181
|
+
throw new AggregateError([], `Reviewer receipt rejected:\n${violations.join("\n")}`, { cause: Object.freeze([...violations]) });
|
|
182
|
+
},
|
|
183
|
+
escalate: async (result) => {
|
|
184
|
+
try { await dependencies.shutdownAgent?.(); } catch (error) { hostActions.failInfrastructure(ledger.recordInfrastructureFailure(error), toolCtx, id); }
|
|
185
|
+
return result;
|
|
186
|
+
},
|
|
187
|
+
auditIncomplete: (result) => result,
|
|
176
188
|
},
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
} });
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
candidate,
|
|
190
|
+
);
|
|
191
|
+
} });
|
|
192
|
+
// Skill invocation transform + agent_start expansion/prompt lifecycle: shared envelope (ADR 0018).
|
|
193
|
+
pi.on("session_shutdown", async () => { try { await dependencies.shutdownAgent?.(); } catch (error) { throw ledger.recordInfrastructureFailure(error); } });
|
|
194
|
+
}
|
|
195
|
+
const activatedSoul = soul;
|
|
196
|
+
const activatedBase = fixedBaseRevision;
|
|
197
|
+
const activatedBinding = binding;
|
|
198
|
+
return Object.freeze({
|
|
199
|
+
dispatcher,
|
|
200
|
+
fixedBaseRevision: activatedBase,
|
|
201
|
+
soul: activatedSoul,
|
|
202
|
+
skillBinding: activatedBinding,
|
|
203
|
+
getSpecDisposition() {
|
|
204
|
+
return acceptedDispatch?.specDisposition;
|
|
205
|
+
},
|
|
193
206
|
});
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return Object.freeze({ dispatcher, fixedBaseRevision });
|
|
197
|
-
} };
|
|
207
|
+
},
|
|
208
|
+
};
|
|
198
209
|
}
|
|
@@ -52,6 +52,10 @@ export function assembleRuntimeReviewerReceipt(input: {
|
|
|
52
52
|
identity: accepted.identity,
|
|
53
53
|
legs: accepted.legs.map(({ axis, prompt }) => ({ axis, prompt: receiptPrompt(prompt) })),
|
|
54
54
|
},
|
|
55
|
+
// Honest Spec skipped/missing notation when independent discovery confirmed absence.
|
|
56
|
+
...(accepted.specDisposition === undefined
|
|
57
|
+
? {}
|
|
58
|
+
: { specDisposition: accepted.specDisposition }),
|
|
55
59
|
}),
|
|
56
60
|
reports,
|
|
57
61
|
outcomes,
|
package/src/role-runtime.ts
CHANGED
|
@@ -59,9 +59,125 @@ import {
|
|
|
59
59
|
import {
|
|
60
60
|
createReviewerRoleRuntime,
|
|
61
61
|
type ReviewerActivation,
|
|
62
|
+
type ReviewerAdmittedInputs,
|
|
62
63
|
} from "./reviewer-role.ts";
|
|
63
64
|
import type { AcceptedReviewerExecution, ReviewerPinnedGitReader } from "./reviewer-dispatch.ts";
|
|
64
65
|
import type { ReviewerDispatchRunResult } from "./reviewer-agent.ts";
|
|
66
|
+
import {
|
|
67
|
+
REVIEWER_VERIFICATION_BOUNDARY,
|
|
68
|
+
type ReviewerSpecDisposition,
|
|
69
|
+
} from "./reviewer-construction.ts";
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Private transport flag names/definitions for Reviewer admitted inputs.
|
|
73
|
+
* Shared activation envelope owns registration and decoding (ADR 0018).
|
|
74
|
+
*/
|
|
75
|
+
const REVIEWER_TRANSPORT_FLAGS = Object.freeze([
|
|
76
|
+
Object.freeze({
|
|
77
|
+
name: "ak-review-base",
|
|
78
|
+
definition: Object.freeze({
|
|
79
|
+
description: "Fixed base revision for the pinned review target",
|
|
80
|
+
type: "string" as const,
|
|
81
|
+
}),
|
|
82
|
+
}),
|
|
83
|
+
Object.freeze({
|
|
84
|
+
name: "ak-review-scope-keys",
|
|
85
|
+
definition: Object.freeze({
|
|
86
|
+
description: "Optional comma-separated exact class keys limiting Reviewer scope",
|
|
87
|
+
type: "string" as const,
|
|
88
|
+
}),
|
|
89
|
+
}),
|
|
90
|
+
Object.freeze({
|
|
91
|
+
name: "ak-review-authority-refs",
|
|
92
|
+
definition: Object.freeze({
|
|
93
|
+
description: "JSON array of durable authority references for Spec evidence-child material only",
|
|
94
|
+
type: "string" as const,
|
|
95
|
+
}),
|
|
96
|
+
}),
|
|
97
|
+
] as const);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Decode private transport flags into frozen admitted inputs.
|
|
101
|
+
* Envelope-owned; necessary JSON decode only (public --authority-ref owns grammar).
|
|
102
|
+
*/
|
|
103
|
+
function decodeReviewerAdmittedInputs(getFlag: (name: string) => unknown): ReviewerAdmittedInputs {
|
|
104
|
+
let reviewScopeKeys: readonly string[] | undefined;
|
|
105
|
+
const rawScopeKeys = getFlag("ak-review-scope-keys");
|
|
106
|
+
if (rawScopeKeys !== undefined) {
|
|
107
|
+
if (typeof rawScopeKeys !== "string" || rawScopeKeys.length === 0) {
|
|
108
|
+
throw new Error("Reviewer scope keys must be a nonempty comma-separated string");
|
|
109
|
+
}
|
|
110
|
+
const parsed = rawScopeKeys.split(",");
|
|
111
|
+
if (parsed.some((key) => key.trim().length === 0) || new Set(parsed).size !== parsed.length) {
|
|
112
|
+
throw new Error("Reviewer scope keys contain a blank or exact duplicate key");
|
|
113
|
+
}
|
|
114
|
+
reviewScopeKeys = Object.freeze(parsed);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let authorityRefs: readonly string[] | undefined;
|
|
118
|
+
const rawAuthorityRefs = getFlag("ak-review-authority-refs");
|
|
119
|
+
if (rawAuthorityRefs !== undefined) {
|
|
120
|
+
if (typeof rawAuthorityRefs !== "string") {
|
|
121
|
+
throw new Error("Reviewer authority refs transport error: flag value must be a string");
|
|
122
|
+
}
|
|
123
|
+
let parsed: unknown;
|
|
124
|
+
try {
|
|
125
|
+
parsed = JSON.parse(rawAuthorityRefs);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Reviewer authority refs transport error: JSON decode failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (!Array.isArray(parsed) || parsed.some((ref) => typeof ref !== "string")) {
|
|
132
|
+
throw new Error("Reviewer authority refs transport error: expected a JSON array of strings");
|
|
133
|
+
}
|
|
134
|
+
authorityRefs = Object.freeze(parsed as string[]);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const baseRevision = getFlag("ak-review-base");
|
|
138
|
+
if (typeof baseRevision !== "string" || !baseRevision.trim()) {
|
|
139
|
+
throw new Error("Reviewer role requires --ak-review-base");
|
|
140
|
+
}
|
|
141
|
+
return Object.freeze({
|
|
142
|
+
baseRevision,
|
|
143
|
+
...(reviewScopeKeys === undefined ? {} : { reviewScopeKeys }),
|
|
144
|
+
...(authorityRefs === undefined ? {} : { authorityRefs }),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Parent system-prompt assembly for the shared activation envelope.
|
|
150
|
+
* References REVIEWER_VERIFICATION_BOUNDARY as the single text true source (no copy).
|
|
151
|
+
*/
|
|
152
|
+
function assembleReviewerParentSystemPrompt(input: {
|
|
153
|
+
baseSystemPrompt: string;
|
|
154
|
+
soul: string;
|
|
155
|
+
specDisposition?: ReviewerSpecDisposition;
|
|
156
|
+
}): string {
|
|
157
|
+
const specDispositionNote =
|
|
158
|
+
input.specDisposition === "skipped-missing"
|
|
159
|
+
? [
|
|
160
|
+
"",
|
|
161
|
+
"<reviewer_spec_disposition>",
|
|
162
|
+
"Spec-Disposition: skipped-missing",
|
|
163
|
+
"Independent discovery confirmed authoritative Spec is absent.",
|
|
164
|
+
"No Spec evidence-child was launched. Note Spec skipped/missing honestly in the final report; do not invent requirements.",
|
|
165
|
+
"</reviewer_spec_disposition>",
|
|
166
|
+
]
|
|
167
|
+
: [];
|
|
168
|
+
return [
|
|
169
|
+
input.baseSystemPrompt,
|
|
170
|
+
"",
|
|
171
|
+
"<reviewer_soul>",
|
|
172
|
+
input.soul,
|
|
173
|
+
"</reviewer_soul>",
|
|
174
|
+
"",
|
|
175
|
+
"<reviewer_verification_boundary>",
|
|
176
|
+
REVIEWER_VERIFICATION_BOUNDARY,
|
|
177
|
+
"</reviewer_verification_boundary>",
|
|
178
|
+
...specDispositionNote,
|
|
179
|
+
].join("\n");
|
|
180
|
+
}
|
|
65
181
|
import {
|
|
66
182
|
CODER_OUTPUT_TOOL_NAME,
|
|
67
183
|
createCoderRoleRuntime,
|
|
@@ -214,7 +330,16 @@ type ActivationRuntime = {
|
|
|
214
330
|
judge: { activate(): Promise<void> };
|
|
215
331
|
fixer: WorkerArmable;
|
|
216
332
|
coder: WorkerArmable;
|
|
217
|
-
reviewer: {
|
|
333
|
+
reviewer: {
|
|
334
|
+
activate(
|
|
335
|
+
context: ExtensionContext,
|
|
336
|
+
admitted: ReviewerAdmittedInputs,
|
|
337
|
+
): Promise<ReviewerActivation>;
|
|
338
|
+
};
|
|
339
|
+
/** Envelope decodes Reviewer transport flags inside the activation stage. */
|
|
340
|
+
decodeReviewerAdmitted(): ReviewerAdmittedInputs;
|
|
341
|
+
/** Envelope stores live parent activation for agent_start prompt assembly. */
|
|
342
|
+
bindReviewerParent(activation: ReviewerActivation): void;
|
|
218
343
|
collector: { activate(context: ExtensionContext, event: { reason: string }): Promise<void> };
|
|
219
344
|
doctor: { activate(): Promise<void> };
|
|
220
345
|
merger(): Promise<void>;
|
|
@@ -226,7 +351,10 @@ function activationStage(role: PackagedRole, runtime: ActivationRuntime): { id:
|
|
|
226
351
|
case "fixer": return { id: "load-and-install", run: async () => runtime.fixer.activate() };
|
|
227
352
|
case "coder": return { id: "load-and-install", run: async () => runtime.coder.activate(runtime.context) };
|
|
228
353
|
case "reviewer": return { id: "load-install-and-dispatch", run: async () => {
|
|
229
|
-
|
|
354
|
+
// Envelope owns flag decode inside the activation stage (trace + fail-closed path).
|
|
355
|
+
const admitted = runtime.decodeReviewerAdmitted();
|
|
356
|
+
const activation = await runtime.reviewer.activate(runtime.context, admitted);
|
|
357
|
+
runtime.bindReviewerParent(activation);
|
|
230
358
|
const result = await activation.dispatcher.dispatch(activation.fixedBaseRevision, { context: runtime.context });
|
|
231
359
|
if (result.status !== "accepted") {
|
|
232
360
|
const rejection = new ExplicitInternalActivationError(
|
|
@@ -419,9 +547,18 @@ export function createRoleRuntimeExtension(
|
|
|
419
547
|
// #102: one shared package-owned tool registration surface for all role runtimes.
|
|
420
548
|
installPackageOwnedToolRegistration(pi);
|
|
421
549
|
pi.registerFlag(ROLE_FLAG.name, ROLE_FLAG.definition);
|
|
550
|
+
// Reviewer transport flags: shared envelope owns registration (ADR 0018).
|
|
551
|
+
for (const flag of REVIEWER_TRANSPORT_FLAGS) {
|
|
552
|
+
pi.registerFlag(flag.name, flag.definition);
|
|
553
|
+
}
|
|
422
554
|
|
|
423
555
|
let admitted = false;
|
|
424
556
|
let selectedRole: string | undefined;
|
|
557
|
+
/** Live Reviewer parent activation for envelope agent_start prompt assembly. */
|
|
558
|
+
let activeReviewerParent: ReviewerActivation | undefined;
|
|
559
|
+
/** Envelope-owned Reviewer Skill expansion state (ADR 0018 — not a role-module facade). */
|
|
560
|
+
let reviewerOriginalRequest: string | undefined;
|
|
561
|
+
let reviewerExpansionCaptured = false;
|
|
425
562
|
let navigatorAttendance: NavigatorAttendanceDependency | undefined;
|
|
426
563
|
let pendingNavigatorPresentation: { event: import("./navigator-attendance.ts").NavigatorEvent; report: import("./navigator-attendance.ts").NavigatorReport } | undefined;
|
|
427
564
|
let pendingNavigatorSettlement: Promise<void> | undefined;
|
|
@@ -431,9 +568,23 @@ export function createRoleRuntimeExtension(
|
|
|
431
568
|
// terminating-tool rejections and mechanical delivery requests share two turns.
|
|
432
569
|
let receiptDelivery = createReceiptDeliveryPolicy();
|
|
433
570
|
let noReceiptRecorded = false;
|
|
434
|
-
pi.on("input", () => {
|
|
571
|
+
pi.on("input", (event) => {
|
|
435
572
|
const role = pi.getFlag(ROLE_FLAG.name);
|
|
436
573
|
if (role !== undefined && !admitted) return { action: "handled" as const };
|
|
574
|
+
// Envelope exclusively owns Reviewer Skill invocation transform + original-request capture.
|
|
575
|
+
if (
|
|
576
|
+
role === "reviewer"
|
|
577
|
+
&& admitted
|
|
578
|
+
&& activeReviewerParent !== undefined
|
|
579
|
+
&& reviewerOriginalRequest === undefined
|
|
580
|
+
) {
|
|
581
|
+
reviewerOriginalRequest = event.text;
|
|
582
|
+
return {
|
|
583
|
+
action: "transform" as const,
|
|
584
|
+
text: activeReviewerParent.skillBinding.invocation(event.text),
|
|
585
|
+
...(event.images === undefined ? {} : { images: event.images }),
|
|
586
|
+
};
|
|
587
|
+
}
|
|
437
588
|
return { action: "continue" as const };
|
|
438
589
|
});
|
|
439
590
|
pi.on("before_agent_start", (event, ctx) => {
|
|
@@ -469,6 +620,29 @@ export function createRoleRuntimeExtension(
|
|
|
469
620
|
}
|
|
470
621
|
}
|
|
471
622
|
navigatorAttendance?.prepare();
|
|
623
|
+
// Envelope-owned Reviewer expansion capture + parent prompt assembly (no role-module callback).
|
|
624
|
+
if (role === "reviewer" && activeReviewerParent !== undefined) {
|
|
625
|
+
if (!reviewerExpansionCaptured) {
|
|
626
|
+
if (
|
|
627
|
+
reviewerOriginalRequest === undefined
|
|
628
|
+
|| activeReviewerParent.skillBinding.captureExpansion(event.prompt, reviewerOriginalRequest) === undefined
|
|
629
|
+
) {
|
|
630
|
+
failInfrastructure(
|
|
631
|
+
new Error("Canonical code-review Skill expansion did not match the captured request"),
|
|
632
|
+
ctx,
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
reviewerExpansionCaptured = true;
|
|
636
|
+
}
|
|
637
|
+
const specDisposition = activeReviewerParent.getSpecDisposition();
|
|
638
|
+
return {
|
|
639
|
+
systemPrompt: assembleReviewerParentSystemPrompt({
|
|
640
|
+
baseSystemPrompt: event.systemPrompt,
|
|
641
|
+
soul: activeReviewerParent.soul,
|
|
642
|
+
...(specDisposition === undefined ? {} : { specDisposition }),
|
|
643
|
+
}),
|
|
644
|
+
};
|
|
645
|
+
}
|
|
472
646
|
});
|
|
473
647
|
pi.on("tool_result", async (event) => {
|
|
474
648
|
const role = selectedRole;
|
|
@@ -839,6 +1013,9 @@ export function createRoleRuntimeExtension(
|
|
|
839
1013
|
pi.on("session_start", async (event, ctx) => {
|
|
840
1014
|
admitted = false;
|
|
841
1015
|
selectedRole = undefined;
|
|
1016
|
+
activeReviewerParent = undefined;
|
|
1017
|
+
reviewerOriginalRequest = undefined;
|
|
1018
|
+
reviewerExpansionCaptured = false;
|
|
842
1019
|
receiptDelivery = createReceiptDeliveryPolicy();
|
|
843
1020
|
noReceiptRecorded = false;
|
|
844
1021
|
observationFace.reset();
|
|
@@ -862,6 +1039,12 @@ export function createRoleRuntimeExtension(
|
|
|
862
1039
|
fixer,
|
|
863
1040
|
coder,
|
|
864
1041
|
reviewer,
|
|
1042
|
+
decodeReviewerAdmitted() {
|
|
1043
|
+
return decodeReviewerAdmittedInputs((name) => pi.getFlag(name));
|
|
1044
|
+
},
|
|
1045
|
+
bindReviewerParent(activation) {
|
|
1046
|
+
activeReviewerParent = activation;
|
|
1047
|
+
},
|
|
865
1048
|
collector,
|
|
866
1049
|
doctor,
|
|
867
1050
|
merger: async () => {
|