@emiliosp/pi-maestro 0.1.1 → 0.1.2

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.
@@ -9,6 +9,7 @@ class MaestroSessionState {
9
9
  private active = false;
10
10
  private activeSpecId: string | null = null;
11
11
  private specSha256: string | null = null;
12
+ private verifierCheckpointCommit: string | null = null;
12
13
 
13
14
  public isActive = (): boolean => this.active;
14
15
 
@@ -34,6 +35,17 @@ class MaestroSessionState {
34
35
  this.specSha256 = await getFileSha256(specPath);
35
36
  };
36
37
 
38
+ public getVerifierCheckpointCommit = (): string | null =>
39
+ this.verifierCheckpointCommit;
40
+
41
+ public setVerifierCheckpointCommit = (commit: string): void => {
42
+ this.verifierCheckpointCommit = commit;
43
+ };
44
+
45
+ public clearVerifierCheckpointCommit = (): void => {
46
+ this.verifierCheckpointCommit = null;
47
+ };
48
+
37
49
  public activate = (): void => {
38
50
  this.active = true;
39
51
  };
@@ -41,6 +53,7 @@ class MaestroSessionState {
41
53
  public clearActiveSpecId = (): void => {
42
54
  this.activeSpecId = null;
43
55
  this.specSha256 = null;
56
+ this.clearVerifierCheckpointCommit();
44
57
  };
45
58
 
46
59
  public deactivate = (): void => {
@@ -6,7 +6,7 @@
6
6
  import type { ExtensionContext } from '@earendil-works/pi-coding-agent';
7
7
  import maestroSessionState from '#maestro/session/MaestroSessionState.ts';
8
8
  import { formatMaestroStatus } from '#maestro/status/formatMaestroStatus.ts';
9
- import { resolveToolLaunchContext } from '#tools/utils/resolveToolLaunchContext.ts';
9
+ import { resolveToolRunContext } from '#tools/utils/resolveToolRunContext.ts';
10
10
  import { readWorkflowState } from '#workflow/state/readWorkflowState.ts';
11
11
 
12
12
  export const MAESTRO_STATUS_KEY = 'maestro';
@@ -32,7 +32,7 @@ export const refreshMaestroStatus = async (
32
32
  }
33
33
 
34
34
  try {
35
- const { paths } = await resolveToolLaunchContext(context.cwd);
35
+ const { paths } = await resolveToolRunContext(context.cwd);
36
36
 
37
37
  const state = await readWorkflowState(paths.getWorkflowPath(specId));
38
38
 
@@ -11,10 +11,9 @@ import {
11
11
  VerifierFindingSchema,
12
12
  } from '#artifacts/verifier-handoff/schema.ts';
13
13
  import { createWorkflowCheckpointCommit } from '#git/commits/createWorkflowCheckpointCommit.ts';
14
- import { getParentCommit } from '#git/history/getParentCommit.ts';
15
- import { getHeadCommit } from '#git/repository/getHeadCommit.ts';
16
14
  import { getRepositoryStatus } from '#git/repository/getRepositoryStatus.ts';
17
15
  import { SPEC_ID_PATTERN } from '#ids/isValidSpecId.ts';
16
+ import maestroSessionState from '#maestro/session/MaestroSessionState.ts';
18
17
  import { resolveWorkflowContext } from '#tools/child/utils/resolveWorkflowContext.ts';
19
18
  import { readWorkflowState } from '#workflow/state/readWorkflowState.ts';
20
19
  import {
@@ -55,6 +54,14 @@ const isVerifierPassRejection = (
55
54
  result: VerifierPassResult,
56
55
  ): result is VerifierPassRejection => 'error' in result;
57
56
 
57
+ function assertVerifierCheckpointCommit(
58
+ commit: string | null,
59
+ ): asserts commit is string {
60
+ if (commit === null) {
61
+ throw new Error('Verifier handoff requires a live verifier checkpoint.');
62
+ }
63
+ }
64
+
58
65
  export const registerRecordVerifierHandoffTool = (pi: ExtensionAPI): void => {
59
66
  pi.registerTool({
60
67
  name: VERIFIER_HANDOFF_TOOL.NAME,
@@ -73,12 +80,9 @@ export const registerRecordVerifierHandoffTool = (pi: ExtensionAPI): void => {
73
80
  paths.getWorkflowPath(specId),
74
81
  );
75
82
 
76
- const headCommit = await getHeadCommit(repositoryRoot);
83
+ const candidateCommit = maestroSessionState.getVerifierCheckpointCommit();
77
84
 
78
- const candidateCommit = await getParentCommit({
79
- repositoryRoot,
80
- commit: headCommit,
81
- });
85
+ assertVerifierCheckpointCommit(candidateCommit);
82
86
 
83
87
  const handoff = {
84
88
  version: VERIFIER_HANDOFF_VERSION,
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { isValidSpecId } from '#ids/isValidSpecId.ts';
7
- import { resolveToolLaunchContext } from '#tools/utils/resolveToolLaunchContext.ts';
7
+ import { resolveToolRunContext } from '#tools/utils/resolveToolRunContext.ts';
8
8
 
9
9
  export type ResolveWorkflowContextInput = {
10
10
  cwd: string;
@@ -19,7 +19,7 @@ export const resolveWorkflowContext = async ({
19
19
  throw new Error(`Invalid spec ID: "${specId}".`);
20
20
  }
21
21
 
22
- const { paths, repositoryRoot } = await resolveToolLaunchContext(cwd);
22
+ const { paths, repositoryRoot } = await resolveToolRunContext(cwd);
23
23
 
24
24
  return { paths, specId, repositoryRoot };
25
25
  };
@@ -7,7 +7,7 @@ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
7
7
  import { Type } from 'typebox';
8
8
  import maestroSessionState from '#maestro/session/MaestroSessionState.ts';
9
9
  import { createSpec } from '#specs/create.ts';
10
- import { resolveToolLaunchContext } from '#tools/utils/resolveToolLaunchContext.ts';
10
+ import { resolveToolRunContext } from '#tools/utils/resolveToolRunContext.ts';
11
11
 
12
12
  export const CREATE_SPEC_TOOL = {
13
13
  NAME: 'maestro_create_spec',
@@ -30,7 +30,7 @@ export const registerCreateSpecTool = (pi: ExtensionAPI): void => {
30
30
  description: CREATE_SPEC_TOOL.DESCRIPTION,
31
31
  parameters: CreateSpecToolParameters,
32
32
  async execute(_toolCallId, { title }, _signal, _onUpdate, context) {
33
- const { paths } = await resolveToolLaunchContext(context.cwd);
33
+ const { paths } = await resolveToolRunContext(context.cwd);
34
34
 
35
35
  const created = await createSpec({
36
36
  paths,
@@ -7,14 +7,14 @@ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
7
7
  import { Type } from 'typebox';
8
8
  import { SPEC_ID_PATTERN } from '#ids/isValidSpecId.ts';
9
9
  import maestroSessionState from '#maestro/session/MaestroSessionState.ts';
10
- import { resolveToolLaunchContext } from '#tools/utils/resolveToolLaunchContext.ts';
10
+ import { resolveToolRunContext } from '#tools/utils/resolveToolRunContext.ts';
11
11
  import { markSpecReady } from '#workflow/spec/markSpecReady.ts';
12
12
 
13
13
  export const MARK_SPEC_READY_TOOL = {
14
14
  NAME: 'maestro_mark_spec_ready',
15
15
  LABEL: 'Mark Spec Ready',
16
16
  DESCRIPTION:
17
- 'Approve the current spec.md after explicit owner approval and move the workflow to ready-for-builder. The owner must commit spec.md and workflow.json before launching the builder.',
17
+ 'Approve the current spec.md after explicit owner approval and move the workflow to ready-for-builder. The owner must commit spec.md and workflow.json before running the builder.',
18
18
  } as const;
19
19
 
20
20
  const MarkSpecReadyToolParameters = Type.Object(
@@ -31,7 +31,7 @@ export const registerMarkSpecReadyTool = (pi: ExtensionAPI): void => {
31
31
  description: MARK_SPEC_READY_TOOL.DESCRIPTION,
32
32
  parameters: MarkSpecReadyToolParameters,
33
33
  async execute(_toolCallId, { specId }, _signal, _onUpdate, context) {
34
- const { paths } = await resolveToolLaunchContext(context.cwd);
34
+ const { paths } = await resolveToolRunContext(context.cwd);
35
35
 
36
36
  const state = await markSpecReady({
37
37
  paths,
@@ -43,7 +43,7 @@ export const registerMarkSpecReadyTool = (pi: ExtensionAPI): void => {
43
43
  content: [
44
44
  {
45
45
  type: 'text',
46
- text: `Spec ${state.specId} is approved at workflow revision ${state.revision} and ready-for-builder. Commit spec.md and workflow.json before launching the builder.`,
46
+ text: `Spec ${state.specId} is approved at workflow revision ${state.revision} and ready-for-builder. Commit spec.md and workflow.json before running the builder.`,
47
47
  },
48
48
  ],
49
49
  details: state,
@@ -10,14 +10,14 @@ import {
10
10
  EscalationResolutionSchema,
11
11
  } from '#artifacts/escalation/schema.ts';
12
12
  import { SPEC_ID_PATTERN } from '#ids/isValidSpecId.ts';
13
- import { resolveToolLaunchContext } from '#tools/utils/resolveToolLaunchContext.ts';
13
+ import { resolveToolRunContext } from '#tools/utils/resolveToolRunContext.ts';
14
14
  import { resolveBuilderEscalation } from '#workflow/escalation/resolveBuilderEscalation.ts';
15
15
 
16
16
  export const RESOLVE_ESCALATION_TOOL = {
17
17
  NAME: 'maestro_resolve_escalation',
18
18
  LABEL: 'Resolve Escalation',
19
19
  DESCRIPTION:
20
- 'Record the explicit owner decision for the current builder escalation when the approved contract remains valid. Commit the resolution and workflow transition on the current branch, then return ready-for-builder without launching the builder. If the contract must change, edit spec.md and use maestro_mark_spec_ready instead.',
20
+ 'Record the explicit owner decision for the current builder escalation when the approved contract remains valid. Commit the resolution and workflow transition on the current branch, then return ready-for-builder without running the builder. If the contract must change, edit spec.md and use maestro_mark_spec_ready instead.',
21
21
  } as const;
22
22
 
23
23
  const ResolveEscalationToolParameters = Type.Object(
@@ -37,7 +37,7 @@ export const registerResolveEscalationTool = (pi: ExtensionAPI): void => {
37
37
  parameters: ResolveEscalationToolParameters,
38
38
  async execute(_toolCallId, params, _signal, _onUpdate, context) {
39
39
  const { specId, escalationId, ...resolution } = params;
40
- const { paths } = await resolveToolLaunchContext(context.cwd);
40
+ const { paths } = await resolveToolRunContext(context.cwd);
41
41
 
42
42
  const resolved = await resolveBuilderEscalation({
43
43
  paths,
@@ -50,7 +50,7 @@ export const registerResolveEscalationTool = (pi: ExtensionAPI): void => {
50
50
  content: [
51
51
  {
52
52
  type: 'text',
53
- text: `Escalation ${resolved.escalation.id} resolved. Spec ${resolved.state.specId} is ready-for-builder. Launch the builder separately.`,
53
+ text: `Escalation ${resolved.escalation.id} resolved. Spec ${resolved.state.specId} is ready-for-builder. Run the builder separately.`,
54
54
  },
55
55
  ],
56
56
  details: {
@@ -7,7 +7,7 @@ import { StringEnum } from '@earendil-works/pi-ai';
7
7
  import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
8
8
  import { Type } from 'typebox';
9
9
  import { SPEC_ID_PATTERN } from '#ids/isValidSpecId.ts';
10
- import { resolveToolLaunchContext } from '#tools/utils/resolveToolLaunchContext.ts';
10
+ import { resolveToolRunContext } from '#tools/utils/resolveToolRunContext.ts';
11
11
  import {
12
12
  FINDING_DECISIONS,
13
13
  resolveFindings,
@@ -73,7 +73,7 @@ const formatResolution = (details: FindingResolutionDetails): string => {
73
73
  const requiringFix = formatFindingIds(details.findingsRequiringFixIds);
74
74
 
75
75
  if (details.phase === WORKFLOW_PHASES.READY_FOR_BUILDER) {
76
- return `Recorded rejections for ${rejected}. Findings requiring code fixes: ${requiringFix}. The workflow is ready-for-builder. Launch the builder separately.`;
76
+ return `Recorded rejections for ${rejected}. Findings requiring code fixes: ${requiringFix}. The workflow is ready-for-builder. Run the builder separately.`;
77
77
  }
78
78
 
79
79
  return `Recorded rejections for ${rejected}. Findings requiring code fixes: ${requiringFix}. The workflow is candidate-ready.`;
@@ -92,7 +92,7 @@ export const registerResolveFindingsTool = (pi: ExtensionAPI): void => {
92
92
  _onUpdate,
93
93
  context,
94
94
  ) {
95
- const { paths } = await resolveToolLaunchContext(context.cwd);
95
+ const { paths } = await resolveToolRunContext(context.cwd);
96
96
 
97
97
  const resolved = await resolveFindings({
98
98
  paths,
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Objective: Launch and validate one foreground builder pass for the owner session.
3
- * Used: When the owner invokes the launch-builder tool.
2
+ * Objective: Run and validate one foreground builder pass for the owner session.
3
+ * Used: When the owner invokes the run-builder tool.
4
4
  */
5
5
 
6
6
  import { randomUUID } from 'node:crypto';
@@ -22,20 +22,20 @@ import {
22
22
  assertDelegationResponse,
23
23
  waitForDelegationResponse,
24
24
  } from '#tools/utils/pi-subagent-delegation.ts';
25
- import { resolveToolLaunchContext } from '#tools/utils/resolveToolLaunchContext.ts';
26
- import { prepareBuilderLaunch } from '#workflow/builder/prepareBuilderLauncher.ts';
25
+ import { resolveToolRunContext } from '#tools/utils/resolveToolRunContext.ts';
26
+ import { prepareBuilderRun } from '#workflow/builder/prepareBuilderRun.ts';
27
27
  import { WORKFLOW_ROLES } from '#workflow/roles.ts';
28
28
  import { readWorkflowState } from '#workflow/state/readWorkflowState.ts';
29
29
  import { WORKFLOW_PHASES, type WorkflowState } from '#workflow/state/schema.ts';
30
30
 
31
- export const LAUNCH_BUILDER_TOOL = {
32
- NAME: 'maestro_launch_builder',
33
- LABEL: 'Launch Builder',
31
+ export const RUN_BUILDER_TOOL = {
32
+ NAME: 'maestro_run_builder',
33
+ LABEL: 'Run Builder',
34
34
  DESCRIPTION:
35
- 'Launch the builder in the current checkout for an owner-approved spec. The builder runs in the foreground and cannot be retried after a committed failure.',
35
+ 'Run the builder in the current checkout for an owner-approved spec. The builder runs in the foreground and cannot be retried after a committed failure.',
36
36
  } as const;
37
37
 
38
- const LaunchBuilderToolParameters = Type.Object(
38
+ const RunBuilderToolParameters = Type.Object(
39
39
  {
40
40
  specId: Type.String({ pattern: SPEC_ID_PATTERN.source }),
41
41
  },
@@ -44,7 +44,7 @@ const LaunchBuilderToolParameters = Type.Object(
44
44
 
45
45
  const MILLISECONDS_PER_MINUTE = 60_000;
46
46
 
47
- type BuilderLaunchResult =
47
+ type BuilderRunResult =
48
48
  | {
49
49
  outcome: typeof BUILDER_HANDOFF_STATUSES.DONE;
50
50
  specId: string;
@@ -75,7 +75,7 @@ type ReadCommittedBuilderResultInput = {
75
75
  const readCommittedBuilderResult = async ({
76
76
  paths,
77
77
  specId,
78
- }: ReadCommittedBuilderResultInput): Promise<BuilderLaunchResult> => {
78
+ }: ReadCommittedBuilderResultInput): Promise<BuilderRunResult> => {
79
79
  try {
80
80
  const repositoryStatus = await getRepositoryStatus(
81
81
  paths.getRepositoryRoot(),
@@ -95,7 +95,7 @@ const readCommittedBuilderResult = async ({
95
95
  );
96
96
  }
97
97
 
98
- return await buildLaunchResult({ paths, specId, state });
98
+ return await buildRunResult({ paths, specId, state });
99
99
  } catch (error) {
100
100
  const message = error instanceof Error ? error.message : String(error);
101
101
 
@@ -117,7 +117,7 @@ const readTerminalBuilderResult = async ({
117
117
  specId,
118
118
  revision,
119
119
  phase,
120
- }: ReadTerminalBuilderResultInput): Promise<BuilderLaunchResult> => {
120
+ }: ReadTerminalBuilderResultInput): Promise<BuilderRunResult> => {
121
121
  const handoff = await readBuilderHandoff({
122
122
  path: paths.getBuilderHandoffPath(specId),
123
123
  specId,
@@ -154,17 +154,17 @@ const readTerminalBuilderResult = async ({
154
154
  };
155
155
  };
156
156
 
157
- type BuildLaunchResultInput = {
157
+ type BuildRunResultInput = {
158
158
  paths: MaestroPaths;
159
159
  specId: string;
160
160
  state: WorkflowState;
161
161
  };
162
162
 
163
- const buildLaunchResult = async ({
163
+ const buildRunResult = async ({
164
164
  paths,
165
165
  specId,
166
166
  state,
167
- }: BuildLaunchResultInput): Promise<BuilderLaunchResult> => {
167
+ }: BuildRunResultInput): Promise<BuilderRunResult> => {
168
168
  if (
169
169
  state.phase === WORKFLOW_PHASES.READY_FOR_VERIFIER ||
170
170
  state.phase === WORKFLOW_PHASES.BUILDER_FAILED
@@ -210,7 +210,7 @@ const buildLaunchResult = async ({
210
210
  );
211
211
  };
212
212
 
213
- const formatBuilderResult = (result: BuilderLaunchResult): string => {
213
+ const formatBuilderResult = (result: BuilderRunResult): string => {
214
214
  if (result.outcome === BUILDER_HANDOFF_STATUSES.DONE) {
215
215
  return `Builder completed spec ${result.specId}. The workflow is ready-for-verifier.`;
216
216
  }
@@ -222,16 +222,16 @@ const formatBuilderResult = (result: BuilderLaunchResult): string => {
222
222
  return `Builder opened escalation ${result.escalation.id} for spec ${result.specId}. The workflow is waiting for an owner decision.`;
223
223
  };
224
224
 
225
- export const registerLaunchBuilderTool = (pi: ExtensionAPI): void => {
225
+ export const registerRunBuilderTool = (pi: ExtensionAPI): void => {
226
226
  pi.registerTool({
227
- name: LAUNCH_BUILDER_TOOL.NAME,
228
- label: LAUNCH_BUILDER_TOOL.LABEL,
229
- description: LAUNCH_BUILDER_TOOL.DESCRIPTION,
230
- parameters: LaunchBuilderToolParameters,
227
+ name: RUN_BUILDER_TOOL.NAME,
228
+ label: RUN_BUILDER_TOOL.LABEL,
229
+ description: RUN_BUILDER_TOOL.DESCRIPTION,
230
+ parameters: RunBuilderToolParameters,
231
231
  async execute(toolCallId, { specId }, _signal, _onUpdate, context) {
232
- const { paths, config } = await resolveToolLaunchContext(context.cwd);
232
+ const { paths, config } = await resolveToolRunContext(context.cwd);
233
233
 
234
- const launch = await prepareBuilderLaunch({ paths, specId });
234
+ const run = await prepareBuilderRun({ paths, specId });
235
235
 
236
236
  const request: SubagentDelegationRequest = {
237
237
  requestId: randomUUID(),
@@ -240,9 +240,9 @@ export const registerLaunchBuilderTool = (pi: ExtensionAPI): void => {
240
240
  // pi-subagents loads this name from agents/builder.md through package.json.
241
241
  // See docs/subagent-integration.md.
242
242
  agent: AGENTS.BUILDER,
243
- task: `Implement specId "${specId}" in the current checkout "${launch.repositoryRoot}". Read all applicable AGENTS.md files before working.`,
243
+ task: `Implement specId "${specId}" in the current checkout "${run.repositoryRoot}". Read all applicable AGENTS.md files before working.`,
244
244
  context: 'fresh',
245
- cwd: launch.repositoryRoot,
245
+ cwd: run.repositoryRoot,
246
246
  model: config.builder.model,
247
247
  thinking: config.builder.thinking,
248
248
  timeoutMs: config.builder.timeoutMinutes * MILLISECONDS_PER_MINUTE,
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Objective: Launch and validate one foreground verifier pass for the owner session.
3
- * Used: When the owner invokes the launch-verifier tool.
2
+ * Objective: Run and validate one foreground verifier pass for the owner session.
3
+ * Used: When the owner invokes the run-verifier tool.
4
4
  */
5
5
 
6
6
  import { randomUUID } from 'node:crypto';
@@ -13,11 +13,12 @@ import { AGENTS } from '#config/schema.ts';
13
13
  import { getRepositoryStatus } from '#git/repository/getRepositoryStatus.ts';
14
14
  import { SPEC_ID_PATTERN } from '#ids/isValidSpecId.ts';
15
15
  import type { MaestroPaths } from '#MaestroPaths.ts';
16
+ import maestroSessionState from '#maestro/session/MaestroSessionState.ts';
16
17
  import {
17
18
  assertDelegationResponse,
18
19
  waitForDelegationResponse,
19
20
  } from '#tools/utils/pi-subagent-delegation.ts';
20
- import { resolveToolLaunchContext } from '#tools/utils/resolveToolLaunchContext.ts';
21
+ import { resolveToolRunContext } from '#tools/utils/resolveToolRunContext.ts';
21
22
  import { WORKFLOW_ROLES } from '#workflow/roles.ts';
22
23
  import { readWorkflowState } from '#workflow/state/readWorkflowState.ts';
23
24
  import { WORKFLOW_PHASES, type WorkflowState } from '#workflow/state/schema.ts';
@@ -27,18 +28,18 @@ import {
27
28
  } from '#workflow/verifier/completeVerifierPass.ts';
28
29
  import { hasProductChanges } from '#workflow/verifier/hasProductChanges.ts';
29
30
  import {
30
- prepareVerifierLaunch,
31
- type VerifierLaunch,
32
- } from '#workflow/verifier/prepareVerifierLaunch.ts';
31
+ prepareVerifierRun,
32
+ type VerifierRun,
33
+ } from '#workflow/verifier/prepareVerifierRun.ts';
33
34
 
34
- export const LAUNCH_VERIFIER_TOOL = {
35
- NAME: 'maestro_launch_verifier',
36
- LABEL: 'Launch Verifier',
35
+ export const RUN_VERIFIER_TOOL = {
36
+ NAME: 'maestro_run_verifier',
37
+ LABEL: 'Run Verifier',
37
38
  DESCRIPTION:
38
- 'Launch the verifier in the current checkout for a completed builder candidate. The verifier runs in the foreground.',
39
+ 'Run the verifier in the current checkout for a completed builder candidate. The verifier runs in the foreground.',
39
40
  } as const;
40
41
 
41
- const LaunchVerifierToolParameters = Type.Object(
42
+ const RunVerifierToolParameters = Type.Object(
42
43
  {
43
44
  specId: Type.String({ pattern: SPEC_ID_PATTERN.source }),
44
45
  },
@@ -78,7 +79,7 @@ type ProductChangesResult = {
78
79
  checkpointCommit: string;
79
80
  };
80
81
 
81
- type VerifierLaunchResult =
82
+ type VerifierRunResult =
82
83
  | CandidateReadyResult
83
84
  | FindingsResult
84
85
  | ProductChangesResult;
@@ -86,7 +87,7 @@ type VerifierLaunchResult =
86
87
  type ReadTerminalVerifierResultInput = {
87
88
  paths: MaestroPaths;
88
89
  specId: string;
89
- launch: VerifierLaunch;
90
+ run: VerifierRun;
90
91
  state: WorkflowState;
91
92
  productChanges: boolean;
92
93
  };
@@ -94,7 +95,7 @@ type ReadTerminalVerifierResultInput = {
94
95
  const readTerminalVerifierResult = async ({
95
96
  paths,
96
97
  specId,
97
- launch,
98
+ run,
98
99
  state,
99
100
  productChanges,
100
101
  }: ReadTerminalVerifierResultInput): Promise<
@@ -115,7 +116,7 @@ const readTerminalVerifierResult = async ({
115
116
  );
116
117
  }
117
118
 
118
- const repositoryStatus = await getRepositoryStatus(launch.repositoryRoot);
119
+ const repositoryStatus = await getRepositoryStatus(run.repositoryRoot);
119
120
 
120
121
  if (!repositoryStatus.clean) {
121
122
  throw new Error(
@@ -143,8 +144,8 @@ const readTerminalVerifierResult = async ({
143
144
  const details = {
144
145
  specId,
145
146
  revision: state.revision,
146
- candidateCommit: launch.candidateCommit,
147
- checkpointCommit: launch.checkpointCommit,
147
+ candidateCommit: run.candidateCommit,
148
+ checkpointCommit: run.checkpointCommit,
148
149
  handoff,
149
150
  };
150
151
 
@@ -158,14 +159,14 @@ const readTerminalVerifierResult = async ({
158
159
  type ReadVerifierResultInput = {
159
160
  paths: MaestroPaths;
160
161
  specId: string;
161
- launch: VerifierLaunch;
162
+ run: VerifierRun;
162
163
  };
163
164
 
164
165
  const readVerifierResult = async ({
165
166
  paths,
166
167
  specId,
167
- launch,
168
- }: ReadVerifierResultInput): Promise<VerifierLaunchResult> => {
168
+ run,
169
+ }: ReadVerifierResultInput): Promise<VerifierRunResult> => {
169
170
  try {
170
171
  const state = await readWorkflowState(paths.getWorkflowPath(specId));
171
172
 
@@ -176,8 +177,8 @@ const readVerifierResult = async ({
176
177
  }
177
178
 
178
179
  const productChanges = await hasProductChanges({
179
- repositoryRoot: launch.repositoryRoot,
180
- candidateCommit: launch.candidateCommit,
180
+ repositoryRoot: run.repositoryRoot,
181
+ candidateCommit: run.candidateCommit,
181
182
  workflowPath: paths.getWorkflowPath(specId),
182
183
  handoffPath: paths.getVerifierHandoffPath(specId),
183
184
  });
@@ -191,8 +192,8 @@ const readVerifierResult = async ({
191
192
  specId,
192
193
  revision: state.revision,
193
194
  phase: state.phase,
194
- candidateCommit: launch.candidateCommit,
195
- checkpointCommit: launch.checkpointCommit,
195
+ candidateCommit: run.candidateCommit,
196
+ checkpointCommit: run.checkpointCommit,
196
197
  };
197
198
  }
198
199
 
@@ -204,7 +205,7 @@ const readVerifierResult = async ({
204
205
  return await readTerminalVerifierResult({
205
206
  paths,
206
207
  specId,
207
- launch,
208
+ run,
208
209
  state,
209
210
  productChanges,
210
211
  });
@@ -215,7 +216,7 @@ const readVerifierResult = async ({
215
216
  }
216
217
  };
217
218
 
218
- const formatVerifierResult = (result: VerifierLaunchResult): string => {
219
+ const formatVerifierResult = (result: VerifierRunResult): string => {
219
220
  if (result.outcome === WORKFLOW_PHASES.CANDIDATE_READY) {
220
221
  return `Verifier completed spec ${result.specId}. The workflow is candidate-ready.`;
221
222
  }
@@ -227,44 +228,48 @@ const formatVerifierResult = (result: VerifierLaunchResult): string => {
227
228
  return `${result.message} The workflow remains verifier-running.`;
228
229
  };
229
230
 
230
- export const registerLaunchVerifierTool = (pi: ExtensionAPI): void => {
231
+ export const registerRunVerifierTool = (pi: ExtensionAPI): void => {
231
232
  pi.registerTool({
232
- name: LAUNCH_VERIFIER_TOOL.NAME,
233
- label: LAUNCH_VERIFIER_TOOL.LABEL,
234
- description: LAUNCH_VERIFIER_TOOL.DESCRIPTION,
235
- parameters: LaunchVerifierToolParameters,
233
+ name: RUN_VERIFIER_TOOL.NAME,
234
+ label: RUN_VERIFIER_TOOL.LABEL,
235
+ description: RUN_VERIFIER_TOOL.DESCRIPTION,
236
+ parameters: RunVerifierToolParameters,
236
237
  async execute(toolCallId, { specId }, _signal, _onUpdate, context) {
237
- const { paths, config } = await resolveToolLaunchContext(context.cwd);
238
-
239
- const launch = await prepareVerifierLaunch({ paths, specId });
240
-
241
- const request: SubagentDelegationRequest = {
242
- requestId: randomUUID(),
243
- ownerRunId: toolCallId,
244
- nodeId: WORKFLOW_ROLES.VERIFIER,
245
- // pi-subagents loads this name from agents/verifier.md through package.json.
246
- agent: AGENTS.VERIFIER,
247
- task: `Verify specId "${specId}" in the current checkout "${launch.repositoryRoot}". Read all applicable AGENTS.md files before working.`,
248
- context: 'fresh',
249
- cwd: launch.repositoryRoot,
250
- model: config.verifier.model,
251
- thinking: config.verifier.thinking,
252
- timeoutMs: config.verifier.timeoutMinutes * MILLISECONDS_PER_MINUTE,
253
- result: { kind: 'text' },
254
- };
255
-
256
- const response = await waitForDelegationResponse({
257
- piEventsBus: pi.events,
258
- request,
259
- });
260
-
261
- assertDelegationResponse(response);
262
- const result = await readVerifierResult({ paths, specId, launch });
263
-
264
- return {
265
- content: [{ type: 'text', text: formatVerifierResult(result) }],
266
- details: result,
267
- };
238
+ const { paths, config } = await resolveToolRunContext(context.cwd);
239
+
240
+ const run = await prepareVerifierRun({ paths, specId });
241
+
242
+ try {
243
+ const request: SubagentDelegationRequest = {
244
+ requestId: randomUUID(),
245
+ ownerRunId: toolCallId,
246
+ nodeId: WORKFLOW_ROLES.VERIFIER,
247
+ // pi-subagents loads this name from agents/verifier.md through package.json.
248
+ agent: AGENTS.VERIFIER,
249
+ task: `Verify specId "${specId}" at candidate checkpoint "${run.candidateCommit}" in the current checkout "${run.repositoryRoot}". Read all applicable AGENTS.md files before working.`,
250
+ context: 'fresh',
251
+ cwd: run.repositoryRoot,
252
+ model: config.verifier.model,
253
+ thinking: config.verifier.thinking,
254
+ timeoutMs: config.verifier.timeoutMinutes * MILLISECONDS_PER_MINUTE,
255
+ result: { kind: 'text' },
256
+ };
257
+
258
+ const response = await waitForDelegationResponse({
259
+ piEventsBus: pi.events,
260
+ request,
261
+ });
262
+
263
+ assertDelegationResponse(response);
264
+ const result = await readVerifierResult({ paths, specId, run });
265
+
266
+ return {
267
+ content: [{ type: 'text', text: formatVerifierResult(result) }],
268
+ details: result,
269
+ };
270
+ } finally {
271
+ maestroSessionState.clearVerifierCheckpointCommit();
272
+ }
268
273
  },
269
274
  });
270
275
  };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Objective: Coordinate foreground pi-subagents delegation responses.
3
- * Used: By Pi tools that launch pi-subagents agents.
3
+ * Used: By Pi tools that run pi-subagents agents.
4
4
  */
5
5
 
6
6
  import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
@@ -8,15 +8,15 @@ import type { MaestroConfig } from '#config/schema.ts';
8
8
  import { findRepositoryRoot } from '#git/repository/findRepositoryRoot.ts';
9
9
  import { MaestroPaths } from '#MaestroPaths.ts';
10
10
 
11
- export type ToolLaunchContext = {
11
+ export type ToolRunContext = {
12
12
  repositoryRoot: string;
13
13
  config: MaestroConfig;
14
14
  paths: MaestroPaths;
15
15
  };
16
16
 
17
- export const resolveToolLaunchContext = async (
17
+ export const resolveToolRunContext = async (
18
18
  cwd: string,
19
- ): Promise<ToolLaunchContext> => {
19
+ ): Promise<ToolRunContext> => {
20
20
  const repositoryRoot = await findRepositoryRoot(cwd);
21
21
  const config = await loadConfiguration(repositoryRoot);
22
22
 
@@ -26,6 +26,8 @@ export async function assertBuilderProtocolUnchanged({
26
26
  const actualSpecSha256 = await getFileSha256(specPath);
27
27
 
28
28
  if (actualSpecSha256 !== expectedSpecSha256) {
29
- throw new Error(`Builder changed spec.md after launch: "${specPath}".`);
29
+ throw new Error(
30
+ `Builder changed spec.md after the run starts: "${specPath}".`,
31
+ );
30
32
  }
31
33
  }