@emiliosp/pi-maestro 0.8.0 → 0.9.1

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/ADR.md CHANGED
@@ -56,3 +56,34 @@ There are no migration procedures, fallback readers, or deprecated aliases. `Art
56
56
  ### Evidence
57
57
 
58
58
  [Repository-only specification](.specs/20261008-132344-store-escalations-in-builder-handoffs/spec.md), [Commit dda6eb5](https://github.com/emilioSp/pi-maestro/commit/dda6eb5b3ab76ba175112c3c565c70be747d7344), and [escalation workflow](docs/workflow.md#escalations).
59
+
60
+ ## ADR-003: Route builder failures through owner escalations
61
+
62
+ ### Status
63
+
64
+ Accepted. Implemented.
65
+
66
+ ### Context
67
+
68
+ The `owner` must decide how work continues after a reportable technical problem.
69
+ The workflow also needs a simpler model with two `builder` outcomes.
70
+
71
+ ### Decision
72
+
73
+ The `builder` reports only `done` or `escalation`.
74
+ Reportable technical failures use the existing `escalation` process.
75
+ The `owner` decides how to proceed instead of reaching a terminal failure phase.
76
+
77
+ ### Consequences
78
+
79
+ Reuse the existing tools, numbered `handoffs`, and batch decisions.
80
+ Individual `probe` results retain `failed` as a valid value.
81
+
82
+ Execution and protocol errors without a valid saved result still stop the workflow.
83
+ This decision adds no automatic recovery.
84
+
85
+ This breaking MVP change has no migrations, compatibility readers, deprecated aliases, or dedicated legacy-format tests.
86
+
87
+ ### Evidence
88
+
89
+ [Specification](.specs/20261013-101454-route-builder-failures-through-owner-escalations/spec.md) and [R-03](ROADMAP.md#r-03-route-builder-failures-through-owner-escalations).
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  This file summarizes meaningful changes to the codebase, including behavior, data formats, architecture, and development tools. Git tags identify version boundaries; GitHub Releases are not required. Version-only changes and routine maintenance are omitted.
4
4
 
5
+ ## 0.9.1
6
+
7
+ 1. Added a brief introduction and an invitation to ask how `Maestro` works to the successful activation notice. See [PR #33](https://github.com/emilioSp/pi-maestro/pull/33).
8
+ 2. Preserved the configured agent details and UI-only activation. Activation does not add session history or start a model request.
9
+ 3. Added absolute paths to the installed README and documentation directory in active instructions. `Maestro` reads relevant documentation on demand without automatically loading its contents.
10
+
11
+ ## 0.9.0
12
+
13
+ 1. Breaking: `builder` reports now support only `done` and `escalation`. Reportable technical problems use the existing `owner` `escalation` process. See [PR #32](https://github.com/emilioSp/pi-maestro/pull/32).
14
+ 2. Removed the terminal `builder` failure phase and event. Execution errors and missing or invalid saved results still stop the workflow without automatic recovery.
15
+ 3. No migrations, compatibility readers, or deprecated aliases are provided. `Artifact` versions remain `1.0.0`.
16
+
17
+ ## 0.8.0
18
+
19
+ 1. Updated the bundled `pi-subagents` dependency from `0.76.0` to `0.78.0`.
20
+ 2. Renamed `test/` to `tests/`. Updated imports, fixture paths, lint configuration, and project instructions. Source tests remain next to their source files.
21
+
5
22
  ## 0.7.3
6
23
 
7
24
  1. Added Oxlint rules for `given ... when ... then ...` test titles, `const` usage, unnecessary `else` branches, type aliases, and ESM imports. See [PR #31](https://github.com/emilioSp/pi-maestro/pull/31).
package/README.md CHANGED
@@ -83,7 +83,8 @@ If a `spec` change is needed during an `escalation` or `finding` decision, the `
83
83
 
84
84
  The workflow ends at `candidate-ready`. Rejected `findings` retain their reasons. Later changes are outside the completed verification. The `owner` controls any later Git use, pull request, and merge.
85
85
 
86
- The same `/maestro` command disables `Maestro` and leaves project files unchanged. Disabling `Maestro`, restarting Pi, or using `/resume` clears live session state. Saved files do not automatically restore or resume an incomplete workflow. The `owner` handles it manually.
86
+ The same `/maestro` command disables `Maestro` and leaves project files unchanged. Disabling `Maestro`, restarting Pi, or using `/resume` clears live session state.
87
+ Saved files do not automatically restore or resume an incomplete workflow. The `owner` handles it manually.
87
88
 
88
89
  ## Configuration
89
90
 
package/ROADMAP.md CHANGED
@@ -24,6 +24,16 @@ Numbered `builder` `handoffs` contain multiple `escalation` questions and their
24
24
 
25
25
  [Specification](.specs/20261008-132344-store-escalations-in-builder-handoffs/spec.md), [verifier report](.specs/20261008-132344-store-escalations-in-builder-handoffs/handoffs/verifier/V1.json), and [merged PR #27](https://github.com/emilioSp/pi-maestro/pull/27).
26
26
 
27
+ ### R-03: Route builder failures through owner escalations
28
+
29
+ Released in `0.9.0`. The `builder` reports only `done` or `escalation`. Reportable technical problems use the existing `owner` `escalation` process. Individual `probe` results retain `failed`. Execution errors and missing or invalid saved results still stop the workflow without automatic recovery.
30
+
31
+ All 8 `acceptance criteria` passed with no `verifier` `findings`. The saved workflow reached `candidate-ready`.
32
+
33
+ #### Evidence
34
+
35
+ [Specification](.specs/20261013-101454-route-builder-failures-through-owner-escalations/spec.md), [verifier report](.specs/20261013-101454-route-builder-failures-through-owner-escalations/handoffs/verifier/V1.json), and [merged PR #32](https://github.com/emilioSp/pi-maestro/pull/32).
36
+
27
37
  ### R-12: Format glossary terms consistently
28
38
 
29
39
  Applied glossary formatting across the documentation and recorded its exclusions in `AGENTS.md` and the documentation audit skill. The approved change document uses the term `spec`. The `owner` confirmed completion.
@@ -32,31 +42,30 @@ Applied glossary formatting across the documentation and recorded its exclusions
32
42
 
33
43
  [Glossary](docs/glossary.md) and [workflow documentation](docs/workflow.md).
34
44
 
35
- ### R-15: Rename the test support directory
45
+ ### R-14: Introduce Maestro at activation
36
46
 
37
- Renamed `test/` to `tests/` at the owner's request. Updated imports, fixture paths, lint configuration, and project instructions. This follows the global instruction to keep fixtures under `tests/fixtures`. Source tests remain next to their source files. Historical `handoffs` retain their recorded paths.
47
+ Implemented in `0.9.1`. npm publication is pending. Successful activation introduces `Maestro` and invites the `owner` to ask how it works. The UI-only notice retains the configured agent details and does not add session history or start a model request.
38
48
 
39
- #### Evidence
49
+ Active instructions identify absolute paths to the installed README and documentation directory. `Maestro` reads relevant documentation on demand without automatically loading its contents.
40
50
 
41
- [Test support](tests/support/), [fixtures](tests/fixtures/), [lint configuration](biome.json), and [project instructions](AGENTS.md#tests).
51
+ All 6 `acceptance criteria` passed with no `verifier` `findings`. The saved workflow reached `candidate-ready`.
42
52
 
43
- ## Now
53
+ #### Evidence
44
54
 
45
- ### R-03: Route builder failures through owner escalations
55
+ [Specification](.specs/20261013-171954-introduce-maestro-at-activation/spec.md), [verifier report](.specs/20261013-171954-introduce-maestro-at-activation/handoffs/verifier/V1.json), and [merged PR #33](https://github.com/emilioSp/pi-maestro/pull/33).
46
56
 
47
- #### Objective
57
+ ### R-15: Rename the test support directory
48
58
 
49
- A technical `builder` failure currently ends the workflow in `builder-failed`. Replace the builder's `failed` `handoff` status and the `builder-failed` workflow phase with an `escalation`. The `owner` decides how to proceed through the existing `escalation` process.
59
+ Renamed `test/` to `tests/` at the owner's request. Updated imports, fixture paths, lint configuration, and project instructions. This follows the global instruction to keep fixtures under `tests/fixtures`. Source tests remain next to their source files. Historical `handoffs` retain their recorded paths.
50
60
 
51
- Automatic recovery of interrupted `runs` is outside this item. No `specification` is approved yet.
61
+ #### Evidence
52
62
 
53
- #### Completed when
63
+ [Test support](tests/support/), [fixtures](tests/fixtures/), [lint configuration](biome.json), and [project instructions](AGENTS.md#tests).
54
64
 
55
- `Builder` failures produce `owner` `escalations` instead of a terminal failure state. Individual checks retain `failed` as a valid result. Agent instructions, tools, workflow transitions, tests, and documentation describe the same behavior.
65
+ ## Now
56
66
 
57
67
  ## Next
58
68
 
59
-
60
69
  ## Later
61
70
 
62
71
  These items need scope review before `specification` approval. Existing behavior must be checked before adding new code. Dependencies between these items are not yet agreed.
@@ -159,16 +168,6 @@ Show an activity indicator in Pi's status bar while progress does not require an
159
168
 
160
169
  The status clearly distinguishes work in progress from a request for `owner` action.
161
170
 
162
- ### R-14: Introduce Maestro at activation
163
-
164
- #### Objective
165
-
166
- When activated, `Maestro` gives a brief introduction and invites the `owner` to ask how it works, if interested. `Maestro` must have access to its own documentation to answer those questions.
167
-
168
- #### Completed when
169
-
170
- Activation includes this short introduction. `Maestro` can consult its documentation.
171
-
172
171
  ### R-16: Verify code quality against AGENTS.md instructions
173
172
 
174
173
  #### Objective
package/agents/builder.md CHANGED
@@ -45,6 +45,7 @@ Follow applicable repository commands and technical rules. Inspect scripts befor
45
45
  Do not edit `spec.md`, prototypes, `workflow.json`, or handoffs directly through any tool.
46
46
  Use `maestro_record_builder_handoff` for protocol changes. The tool supplies artifact paths and versions.
47
47
 
48
+ If a technical problem prevents completion and you can safely report it, stop implementation and submit an escalation. Use its fields for the reason, evidence, owner question, options, consequences, and next steps. The owner decides how work continues.
48
49
  If a significant discovery requires an owner choice, stop implementation and use the escalation outcome below.
49
50
  Examples include a spec conflict, undefined behavior, a material architectural alternative, scope changes, or verification and reversibility decisions.
50
51
  An escalation does not require a technical failure or blocker.
@@ -75,8 +76,7 @@ Before any terminal tool call, restore temporary verification changes and remove
75
76
  Choose the outcome from the actual result:
76
77
 
77
78
  1. `done`: Implementation and required checks are complete. Every criterion has `probeStatus: passed`. Call `maestro_record_builder_handoff` with `specId`, `status: done`, `summary`, `acceptanceCriteria`, `notes`, and `escalations: []`.
78
- 2. `escalation`: An owner decision is required. Call `maestro_record_builder_handoff` with `specId`, `status: escalation`, `summary`, every criterion result, `notes`, and all `escalations`. Assign sequential IDs in array order: `E1`, `E2`, and so on. Restart at `E1` for each handoff. Each entry contains `id`, `question`, `context`, `options`, `recommendation`, `notes`, and `resolution: null`. Include evidence in the context. Give each option an ID, description, consequences, and next step. Use `recommendation: null` unless evidence supports an option. Refer to questions with both IDs, such as `B1/E1`. Record actual probe results, including incomplete probes. All probes can pass when an owner decision remains necessary. Save one handoff and stop.
79
- 3. `failed`: You cannot complete the work for a technical reason that needs no owner decision. Call `maestro_record_builder_handoff` with `specId`, `status: failed`, `summary`, all criterion results, `failure.reason`, and `notes`. Report actual statuses, including `not-run` where applicable.
79
+ 2. `escalation`: An owner decision is required, including when a reportable technical problem prevents completion. Call `maestro_record_builder_handoff` with `specId`, `status: escalation`, `summary`, every criterion result, `notes`, and all `escalations`. Assign sequential IDs in array order: `E1`, `E2`, and so on. Restart at `E1` for each handoff. Each entry contains `id`, `question`, `context`, `options`, `recommendation`, `notes`, and `resolution: null`. Include evidence in the context. Give each option an ID, description, consequences, and next step. Use `recommendation: null` unless evidence supports an option. Refer to questions with both IDs, such as `B1/E1`. Record actual probe results, including incomplete probes. All probes can pass when an owner decision remains necessary or another required repository check prevents completion. Save one handoff and stop.
80
80
 
81
81
  After a successful terminal call, return the saved outcome and artifact path to Maestro. Stop the pass.
82
82
  Do not delete or replace earlier handoffs. Do not continue implementation after saving the result.
@@ -88,8 +88,6 @@ If a terminal call fails, inspect the error, current phase, and artifacts before
88
88
  If validation rejected the submission without writing it, correct the payload to match the tool schema and actual evidence.
89
89
  If the tool partially wrote an artifact or changed phase before an error, report it and stop. Do not resubmit.
90
90
  Never change honest statuses or omit criteria merely to make a payload pass validation.
91
- The tool rejects `failed` when a nonempty criterion list contains only `passed` probes.
92
- If another technical failure prevents completion in that case, report this protocol limitation to Maestro instead of falsifying criterion results.
93
91
  Do not delete a terminal artifact, edit workflow state, or use a different outcome to bypass an error.
94
92
  If cleanup or the protocol cannot be completed safely, report the exact blocker and remaining changes to Maestro and stop.
95
93
  Do not claim a saved result when the terminal call did not succeed.
package/docs/glossary.md CHANGED
@@ -18,7 +18,7 @@
18
18
  | `Run` or `pass` | One execution of the `builder` or `verifier` with a fresh conversation. |
19
19
  | `Candidate` | The project files created by agents after a workflow `run` |
20
20
  | `Finding` | A technical issue that the `verifier` records for an `owner` decision. |
21
- | `Escalation` | An implementation question that the `builder` records in its `handoff` for an `owner` decision. |
21
+ | `Escalation` | An implementation question or reportable technical problem that the `builder` records in its `handoff` for an `owner` decision. |
22
22
  | `GREEN FLAG` | The owner's explicit reply that approves the current `spec` and starts the `builder`. |
23
23
  | `fix-code` | An `owner` decision that requests code fixes without changing the approved `spec`. |
24
24
  | `reject` | An `owner` decision that declines a `finding` and records a reason. It does not mean that verification passed. |
package/docs/workflow.md CHANGED
@@ -17,7 +17,7 @@ A `verifier` `handoff` can contain `findings`, which are technical issues.
17
17
  |---|---|
18
18
  | `Owner` | Decides requirements, scope, technical decisions, `spec` approval, `escalation` answers, and `finding` decisions. Performs the final review. |
19
19
  | `Maestro` | Prepares the `spec` with the `owner`, records workflow progress, runs agents, presents issues, and saves `owner` decisions. |
20
- | `Builder` | Implements the approved `spec` and runs every `probe`. Reports completion, an `escalation`, or a technical failure. |
20
+ | `Builder` | Implements the approved `spec` and runs every `probe`. Reports completion or an `escalation`, including technical problems that prevent completion. |
21
21
  | `Verifier` | Independently runs every `probe` against the live project files. Reports `findings` without repairing product code. |
22
22
 
23
23
  `Builder` and `verifier` `runs` are sequential foreground operations. Each starts with a fresh conversation and reads the `spec` and saved `artifacts`.
@@ -66,8 +66,6 @@ flowchart TD
66
66
  ownerEscalation -->|Spec changes| reviseSpec[Owner and Maestro revise the spec]
67
67
  reviseSpec --> approval
68
68
 
69
- buildOutcome -->|Failed| stopped[Workflow stops for manual owner follow-up]
70
-
71
69
  findings -->|One or more| ownerFindings[Owner decides every finding]
72
70
  ownerFindings -->|Reject all with reasons| candidate
73
71
  ownerFindings -->|Fix code| recordFixes[Maestro records the decisions]
@@ -77,7 +75,7 @@ flowchart TD
77
75
  candidate --> summary
78
76
  summary --> review
79
77
 
80
- linkStyle 0,1,2,3,4,5,17,18 stroke:#2e7d32,stroke-width:3px
78
+ linkStyle 0,1,2,3,4,5,16,17 stroke:#2e7d32,stroke-width:3px
81
79
  ```
82
80
 
83
81
  ## Workflow phases
@@ -92,7 +90,6 @@ The default path is `.specs/<spec-id>/workflow.json`. The `spec` directory is [c
92
90
  | `ready-for-builder` | The approved `spec` or recorded `owner` decisions permit a `builder` `run`. |
93
91
  | `builder-running` | A `builder` `run` is active. |
94
92
  | `escalation-decision` | The `owner` must decide how to handle every `escalation` question. |
95
- | `builder-failed` | The `builder` recorded a technical failure. The workflow stops. |
96
93
  | `ready-for-verifier` | The `builder` completed the work and the `verifier` can start. |
97
94
  | `verifier-running` | A `verifier` `run` is active. |
98
95
  | `findings-decision` | The `owner` must decide how to handle every current `finding`. |
@@ -123,7 +120,8 @@ Each criterion contains these parts:
123
120
 
124
121
  ## Escalations
125
122
 
126
- An `escalation` returns an implementation decision to the `owner`. It does not necessarily mean that a technical failure occurred. Examples include undefined behavior, a conflict with the `spec`, or a possible scope change.
123
+ An `escalation` returns an implementation decision or a technical problem to the `owner`.
124
+ For instance a technical failure, an undefined behavior, a conflict with the `spec`, or a possible scope change.
127
125
 
128
126
  The `builder` saves all questions from one `pass` in the `escalations` list of its numbered `handoff`. `Maestro` identifies each question with both report and `escalation` IDs, such as `B1/E1`. See [Escalation references](glossary.md#escalation-references) for their scope.
129
127
 
@@ -197,7 +195,10 @@ For example, a repair cycle with successful `builder` results produces these fil
197
195
 
198
196
  If the first `builder` `run` escalates, it creates `B1.json` with its questions. `Owner` answers update `B1.json`. The next saved `builder` result creates `B2.json`.
199
197
 
200
- A `builder` report with `status: failed` stops the workflow without a `verifier` `run`. If an agent returns without a valid saved result, `Maestro` stops and reports the error. See [Limitations](#limitations).
198
+ A `builder` report has `status: done` or `status: escalation`. A `done` report requires every recorded `probe` to pass and an empty `escalations` list.
199
+ An `escalation` retains actual `probe` results: `passed`, `failed`, or `not-run`.
200
+
201
+ Execution errors or missing or invalid saved results stop the workflow. See [Limitations](#limitations).
201
202
 
202
203
  The default layout after multiple `runs` is:
203
204
 
@@ -226,7 +227,9 @@ One example is `gpt-luna-6` with `thinking: "low"`.
226
227
  Moreover, the workflow works well with a defined harness: tests, lint for code rules, anti-slop checks for unwanted patterns, and `AGENTS.md`. `Maestro` does not supply these project-specific checks.
227
228
 
228
229
  ### No automatic rollback
229
- `Maestro` provides no automatic rollback, repair, or recovery for failed or interrupted workflows. The files that remain are available for `owner` inspection. The `owner` handles the workflow manually.
230
+ Launch errors, delegation failures, timeouts, interruptions, cancellations, and missing or invalid `handoffs` stop the workflow without a recovery transition.
231
+ If a `run` saves no completed transition, its saved running phase remains unchanged. Partial protocol writes require manual `owner` follow-up.
232
+ `Maestro` provides no automatic rollback, repair, or recovery for these errors. The files that remain are available for `owner` inspection.
230
233
 
231
234
  ### No reconciliation
232
235
  Disabling `Maestro`, restarting Pi, or using `/resume` clears live `Maestro` session state and leaves project files unchanged. `Maestro` starts disabled in a new or resumed session. Reactivating it does not reconstruct or resume a saved workflow.
@@ -90,10 +90,12 @@ export default (pi: ExtensionAPI): void => {
90
90
  syncMainTools();
91
91
  await refreshMaestroStatus(context);
92
92
 
93
- // show builder/verifier model on activation
93
+ // Introduce Maestro and show configured agents only in the UI.
94
94
  context.ui.notify(
95
95
  [
96
96
  context.ui.theme.fg('muted', 'Maestro active'),
97
+ 'Maestro helps the owner define a spec, then coordinates implementation and independent verification.',
98
+ 'Ask how Maestro works for a short explanation.',
97
99
  context.ui.theme.fg(
98
100
  'accent',
99
101
  `Builder (${config.builder.model} ${config.builder.thinking})`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emiliosp/pi-maestro",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "A spec-driven multiagent development workflow for Pi.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -26,11 +26,6 @@ const hasOnlyPassedProbes = (
26
26
  (criterion) => criterion.probeStatus === PROBE_STATUSES.PASSED,
27
27
  );
28
28
 
29
- const hasValidFailedChecks = (
30
- acceptanceCriteria: BuilderAcceptanceCriterion[],
31
- ): boolean =>
32
- acceptanceCriteria.length === 0 || !hasOnlyPassedProbes(acceptanceCriteria);
33
-
34
29
  function assertBuilderHandoffSchema(
35
30
  input: unknown,
36
31
  ): asserts input is BuilderHandoff {
@@ -71,15 +66,6 @@ export function assertBuilderHandoff(
71
66
  throw new Error('Done builder handoff requires every probe to pass.');
72
67
  }
73
68
 
74
- if (
75
- handoff.status === BUILDER_HANDOFF_STATUSES.FAILED &&
76
- !hasValidFailedChecks(handoff.acceptanceCriteria)
77
- ) {
78
- throw new Error(
79
- 'Failed builder handoff cannot mark every acceptance check as completed.',
80
- );
81
- }
82
-
83
69
  if (handoff.status === BUILDER_HANDOFF_STATUSES.ESCALATION) {
84
70
  for (const [index, escalation] of handoff.escalations.entries()) {
85
71
  if (escalation.id !== `E${index + 1}`) {
@@ -11,7 +11,6 @@ export const BUILDER_HANDOFF_VERSION = '1.0.0';
11
11
 
12
12
  export const BUILDER_HANDOFF_STATUSES = {
13
13
  DONE: 'done',
14
- FAILED: 'failed',
15
14
  ESCALATION: 'escalation',
16
15
  } as const;
17
16
 
@@ -41,11 +40,6 @@ export type BuilderAcceptanceCriterion = Static<
41
40
  typeof BuilderAcceptanceCriterionSchema
42
41
  >;
43
42
 
44
- export const BuilderHandoffFailureSchema = Type.Object(
45
- { reason: Type.String({ minLength: 1 }) },
46
- { additionalProperties: false },
47
- );
48
-
49
43
  const BuilderHandoffContentFields = {
50
44
  summary: Type.String({ minLength: 1 }),
51
45
  acceptanceCriteria: Type.Array(BuilderAcceptanceCriterionSchema),
@@ -134,19 +128,9 @@ export const BuilderEscalationHandoffSchema = Type.Object(
134
128
  { additionalProperties: false },
135
129
  );
136
130
 
137
- export const BuilderFailedHandoffSchema = Type.Object(
138
- {
139
- ...BuilderHandoffFields,
140
- status: Type.Literal(BUILDER_HANDOFF_STATUSES.FAILED),
141
- failure: BuilderHandoffFailureSchema,
142
- },
143
- { additionalProperties: false },
144
- );
145
-
146
131
  export const BuilderHandoffSchema = Type.Union([
147
132
  BuilderDoneHandoffSchema,
148
133
  BuilderEscalationHandoffSchema,
149
- BuilderFailedHandoffSchema,
150
134
  ]);
151
135
 
152
136
  export type BuilderHandoff = Static<typeof BuilderHandoffSchema>;
@@ -169,19 +153,9 @@ export const BuilderEscalationHandoffSubmissionSchema = Type.Object(
169
153
  { additionalProperties: false },
170
154
  );
171
155
 
172
- export const BuilderFailedHandoffSubmissionSchema = Type.Object(
173
- {
174
- status: Type.Literal(BUILDER_HANDOFF_STATUSES.FAILED),
175
- ...BuilderHandoffContentFields,
176
- failure: BuilderHandoffFailureSchema,
177
- },
178
- { additionalProperties: false },
179
- );
180
-
181
156
  export const BuilderHandoffSubmissionSchema = Type.Union([
182
157
  BuilderDoneHandoffSubmissionSchema,
183
158
  BuilderEscalationHandoffSubmissionSchema,
184
- BuilderFailedHandoffSubmissionSchema,
185
159
  ]);
186
160
 
187
161
  export type BuilderHandoffSubmissionInput = Static<
@@ -3,6 +3,14 @@
3
3
  * Used: When Pi prepares a model request while Maestro is active.
4
4
  */
5
5
 
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const README_PATH = fileURLToPath(
9
+ new URL('../../../README.md', import.meta.url),
10
+ );
11
+
12
+ const DOCS_PATH = fileURLToPath(new URL('../../../docs/', import.meta.url));
13
+
6
14
  const MAESTRO_INSTRUCTIONS = `## Maestro mode
7
15
 
8
16
  You are Maestro. Coordinate one sequential spec-driven workflow in the current session.
@@ -10,6 +18,14 @@ The owner decides requirements, scope, technical decisions in the spec, spec app
10
18
  The builder implements the approved spec. The verifier independently checks the candidate. Do not perform their work yourself.
11
19
  The approved spec.md is the contract for all three roles. Do not infer owner approval from an agent's recommendation.
12
20
 
21
+ ### Maestro documentation
22
+
23
+ The loaded Maestro package's documentation is separate from the owner's project:
24
+ README.md: ${README_PATH}
25
+ docs/: ${DOCS_PATH}
26
+ Before answering questions about Maestro, use the available file-reading tools to read the relevant documentation on demand.
27
+ Do not automatically load all documentation.
28
+
13
29
  ### Tools and boundaries
14
30
 
15
31
  The project root is the canonical Pi working directory, resolved with realpath. Do not search ancestor directories.
@@ -83,8 +99,8 @@ Read the returned artifact and follow its outcome:
83
99
 
84
100
  1. done: The phase is ready-for-verifier. Call maestro_run_verifier with the active specId.
85
101
  2. escalation: The phase is escalation-decision. Present all questions from the active builder handoff, with evidence, options, consequences, and next steps.
86
- 3. failed: The phase is builder-failed. Report the failure and stop. There is no builder retry or spec revision from this phase.
87
102
 
103
+ Reportable technical problems that prevent builder completion use the existing escalation process. The owner decides how work continues.
88
104
  A significant discovery needs an escalation when the owner must choose between meaningful alternatives, even without a technical blocker.
89
105
  Do not invent an escalation for routine implementation details. Read significant discoveries without owner decisions from the builder handoff notes.
90
106
 
@@ -142,9 +158,11 @@ If a tool fails, inspect its error, workflow.json, and artifacts before taking a
142
158
  If the tool wrote no artifacts or transition, correct recoverable errors within your permissions before retrying.
143
159
  If it partially updated artifacts or workflow state, report that state and stop. Do not retry a partially completed transition.
144
160
  Do not repeat an unchanged failing call, edit protocol files, or fabricate evidence to bypass an error.
161
+ Launch errors, delegation failures, timeouts, interruptions, cancellations, and missing or invalid handoffs stop the workflow. Do not create synthetic escalations or recovery transitions.
145
162
  If a run ends without a valid saved result, report the error and stop. Do not repair product files or force a transition.
163
+ If a run saves no completed transition, its saved running phase remains unchanged.
146
164
  Disabling Maestro, restarting Pi, or using /resume clears live state. Do not reconstruct or resume an incomplete workflow.
147
- The owner handles failed or interrupted workflows manually.
165
+ The owner handles execution errors, protocol errors, and interrupted workflows manually.
148
166
 
149
167
  At candidate-ready, the workflow is complete. No final tool call is required.
150
168
  You own the final summary. Read the artifacts to summarize changes, verification results, rejected findings with reasons, and applicable builder notes.
@@ -15,7 +15,6 @@ export const WORKFLOW_PHASE_LABELS = {
15
15
  [WORKFLOW_PHASES.READY_FOR_BUILDER]: 'Ready for builder',
16
16
  [WORKFLOW_PHASES.BUILDER_RUNNING]: 'Builder running',
17
17
  [WORKFLOW_PHASES.ESCALATION_DECISION]: 'Owner decision needed: escalation',
18
- [WORKFLOW_PHASES.BUILDER_FAILED]: 'Builder failed',
19
18
  [WORKFLOW_PHASES.READY_FOR_VERIFIER]: 'Ready for verifier',
20
19
  [WORKFLOW_PHASES.VERIFIER_RUNNING]: 'Verifier running',
21
20
  [WORKFLOW_PHASES.FINDINGS_DECISION]: 'Owner decision needed: findings',
@@ -27,7 +26,6 @@ export const WORKFLOW_PHASE_ICONS = {
27
26
  [WORKFLOW_PHASES.READY_FOR_BUILDER]: '🚧',
28
27
  [WORKFLOW_PHASES.BUILDER_RUNNING]: '🛠️',
29
28
  [WORKFLOW_PHASES.ESCALATION_DECISION]: '✋',
30
- [WORKFLOW_PHASES.BUILDER_FAILED]: '❌',
31
29
  [WORKFLOW_PHASES.READY_FOR_VERIFIER]: '📋',
32
30
  [WORKFLOW_PHASES.VERIFIER_RUNNING]: '🔍',
33
31
  [WORKFLOW_PHASES.FINDINGS_DECISION]: '💬',
@@ -39,7 +37,6 @@ export const WORKFLOW_PHASE_COLORS = {
39
37
  [WORKFLOW_PHASES.READY_FOR_BUILDER]: 'accent',
40
38
  [WORKFLOW_PHASES.BUILDER_RUNNING]: 'accent',
41
39
  [WORKFLOW_PHASES.ESCALATION_DECISION]: 'warning',
42
- [WORKFLOW_PHASES.BUILDER_FAILED]: 'error',
43
40
  [WORKFLOW_PHASES.READY_FOR_VERIFIER]: 'accent',
44
41
  [WORKFLOW_PHASES.VERIFIER_RUNNING]: 'accent',
45
42
  [WORKFLOW_PHASES.FINDINGS_DECISION]: 'warning',
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Objective: Register the builder handoff tool for the current project.
3
- * Used: When the builder reports a done, escalation, or failed result.
3
+ * Used: When the builder reports a done or escalation result.
4
4
  */
5
5
 
6
6
  import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
@@ -8,7 +8,6 @@ import { Type } from 'typebox';
8
8
  import {
9
9
  BuilderDoneHandoffSubmissionSchema,
10
10
  BuilderEscalationHandoffSubmissionSchema,
11
- BuilderFailedHandoffSubmissionSchema,
12
11
  } from '#artifacts/builder-handoff/schema.ts';
13
12
  import { SPEC_ID_PATTERN } from '#ids/isValidSpecId.ts';
14
13
  import { resolveWorkflowContext } from '#tools/child/utils/resolveWorkflowContext.ts';
@@ -18,7 +17,7 @@ export const BUILDER_HANDOFF_TOOL = {
18
17
  NAME: 'maestro_record_builder_handoff',
19
18
  LABEL: 'Record Builder Handoff',
20
19
  DESCRIPTION:
21
- 'Record the builder pass as done, escalation, or failed. Put significant discoveries that do not require an owner decision in notes. After success, stop. The tool saves a numbered builder handoff and the workflow phase.',
20
+ 'Record the builder pass as done or escalation. Report technical problems that prevent completion as escalations for owner decisions. Put significant discoveries that do not require an owner decision in notes. After success, stop. The tool saves a numbered builder handoff and the workflow phase.',
22
21
  } as const;
23
22
 
24
23
  const BuilderHandoffToolParameters = Type.Union([
@@ -36,13 +35,6 @@ const BuilderHandoffToolParameters = Type.Union([
36
35
  },
37
36
  { additionalProperties: false },
38
37
  ),
39
- Type.Object(
40
- {
41
- specId: Type.String({ pattern: SPEC_ID_PATTERN.source }),
42
- ...BuilderFailedHandoffSubmissionSchema.properties,
43
- },
44
- { additionalProperties: false },
45
- ),
46
38
  ]);
47
39
 
48
40
  export const registerRecordBuilderHandoffTool = (pi: ExtensionAPI): void => {
@@ -11,7 +11,7 @@ import {
11
11
  } from '#artifacts/builder-handoff/schema.ts';
12
12
  import { SPEC_ID_PATTERN } from '#ids/isValidSpecId.ts';
13
13
  import { resolveToolRunContext } from '#tools/utils/resolveToolRunContext.ts';
14
- import { resolveEscalations } from '#workflow/escalation/resolveEscalations.ts';
14
+ import { resolveEscalations } from '#workflow/builder/resolveEscalations.ts';
15
15
 
16
16
  export const RESOLVE_ESCALATIONS_TOOL = {
17
17
  NAME: 'maestro_resolve_escalations',
@@ -9,8 +9,8 @@ import { Type } from 'typebox';
9
9
  import { FINDING_DECISIONS } from '#artifacts/verifier-handoff/schema.ts';
10
10
  import { SPEC_ID_PATTERN } from '#ids/isValidSpecId.ts';
11
11
  import { resolveToolRunContext } from '#tools/utils/resolveToolRunContext.ts';
12
- import { resolveFindings } from '#workflow/findings/resolveFindings.ts';
13
12
  import { WORKFLOW_PHASES } from '#workflow/state/schema.ts';
13
+ import { resolveFindings } from '#workflow/verifier/resolveFindings.ts';
14
14
 
15
15
  export const RESOLVE_FINDINGS_TOOL = {
16
16
  NAME: 'maestro_resolve_findings',
@@ -30,7 +30,7 @@ export const RUN_BUILDER_TOOL = {
30
30
  NAME: 'maestro_run_builder',
31
31
  LABEL: 'Run Builder',
32
32
  DESCRIPTION:
33
- 'Run the builder in the project directory for an owner-approved spec. The builder runs in the foreground and cannot be retried after a recorded failure.',
33
+ 'Run the builder in the project directory for an owner-approved spec. The builder runs in the foreground and returns done or escalation. Owner decisions or renewed spec approval permit another pass after an escalation.',
34
34
  } as const;
35
35
 
36
36
  const RunBuilderToolParameters = Type.Object(
@@ -49,12 +49,6 @@ type BuilderRunResult =
49
49
  phase: typeof WORKFLOW_PHASES.READY_FOR_VERIFIER;
50
50
  handoff: BuilderHandoff;
51
51
  }
52
- | {
53
- outcome: typeof BUILDER_HANDOFF_STATUSES.FAILED;
54
- specId: string;
55
- phase: typeof WORKFLOW_PHASES.BUILDER_FAILED;
56
- handoff: BuilderHandoff;
57
- }
58
52
  | {
59
53
  outcome: typeof BUILDER_HANDOFF_STATUSES.ESCALATION;
60
54
  specId: string;
@@ -88,48 +82,25 @@ const readBuilderResult = async ({
88
82
  }
89
83
  };
90
84
 
91
- type ReadTerminalBuilderResultInput = {
92
- paths: MaestroPaths;
93
- specId: string;
94
- phase:
95
- | typeof WORKFLOW_PHASES.READY_FOR_VERIFIER
96
- | typeof WORKFLOW_PHASES.BUILDER_FAILED;
97
- };
98
-
99
- const readTerminalBuilderResult = async ({
85
+ const readDoneBuilderResult = async ({
100
86
  paths,
101
87
  specId,
102
- phase,
103
- }: ReadTerminalBuilderResultInput): Promise<BuilderRunResult> => {
88
+ }: ReadBuilderResultInput): Promise<BuilderRunResult> => {
104
89
  const handoff = await readBuilderHandoff({
105
90
  path: await paths.getActiveBuilderHandoffPath(specId),
106
91
  specId,
107
92
  });
108
93
 
109
- const expectedStatus =
110
- phase === WORKFLOW_PHASES.READY_FOR_VERIFIER
111
- ? BUILDER_HANDOFF_STATUSES.DONE
112
- : BUILDER_HANDOFF_STATUSES.FAILED;
113
-
114
- if (handoff.status !== expectedStatus) {
94
+ if (handoff.status !== BUILDER_HANDOFF_STATUSES.DONE) {
115
95
  throw new Error(
116
- `Builder handoff status does not match workflow phase "${phase}".`,
96
+ `Builder handoff status does not match workflow phase "${WORKFLOW_PHASES.READY_FOR_VERIFIER}".`,
117
97
  );
118
98
  }
119
99
 
120
- if (phase === WORKFLOW_PHASES.READY_FOR_VERIFIER) {
121
- return {
122
- outcome: BUILDER_HANDOFF_STATUSES.DONE,
123
- specId,
124
- phase,
125
- handoff,
126
- };
127
- }
128
-
129
100
  return {
130
- outcome: BUILDER_HANDOFF_STATUSES.FAILED,
101
+ outcome: BUILDER_HANDOFF_STATUSES.DONE,
131
102
  specId,
132
- phase,
103
+ phase: WORKFLOW_PHASES.READY_FOR_VERIFIER,
133
104
  handoff,
134
105
  };
135
106
  };
@@ -145,15 +116,8 @@ const buildRunResult = async ({
145
116
  specId,
146
117
  state,
147
118
  }: BuildRunResultInput): Promise<BuilderRunResult> => {
148
- if (
149
- state.phase === WORKFLOW_PHASES.READY_FOR_VERIFIER ||
150
- state.phase === WORKFLOW_PHASES.BUILDER_FAILED
151
- ) {
152
- return await readTerminalBuilderResult({
153
- paths,
154
- specId,
155
- phase: state.phase,
156
- });
119
+ if (state.phase === WORKFLOW_PHASES.READY_FOR_VERIFIER) {
120
+ return await readDoneBuilderResult({ paths, specId });
157
121
  }
158
122
 
159
123
  if (state.phase === WORKFLOW_PHASES.ESCALATION_DECISION) {
@@ -192,10 +156,6 @@ const formatBuilderResult = (result: BuilderRunResult): string => {
192
156
  return `Builder completed spec ${result.specId}. The workflow is ready-for-verifier.`;
193
157
  }
194
158
 
195
- if (result.outcome === BUILDER_HANDOFF_STATUSES.FAILED) {
196
- return `Builder failed for spec ${result.specId}. The workflow is builder-failed and cannot be retried.`;
197
- }
198
-
199
159
  return `Builder submitted escalation questions for spec ${result.specId}. The workflow is waiting for an owner decision.`;
200
160
  };
201
161
 
@@ -1,13 +1,12 @@
1
1
  /**
2
2
  * Objective: Complete a builder pass with a validated terminal handoff.
3
- * Used: When the builder reports done, escalation, or failed through the child tool.
3
+ * Used: When the builder reports done or escalation through the child tool.
4
4
  */
5
5
 
6
6
  import { mkdir } from 'node:fs/promises';
7
7
  import { Value } from 'typebox/value';
8
8
  import { assertBuilderHandoff } from '#artifacts/builder-handoff/assertBuilderHandoff.ts';
9
9
  import {
10
- BUILDER_HANDOFF_STATUSES,
11
10
  BUILDER_HANDOFF_VERSION,
12
11
  type BuilderHandoff,
13
12
  type BuilderHandoffSubmissionInput,
@@ -15,12 +14,9 @@ import {
15
14
  } from '#artifacts/builder-handoff/schema.ts';
16
15
  import { writeBuilderHandoff } from '#artifacts/builder-handoff/writeBuilderHandoff.ts';
17
16
  import type { MaestroPaths } from '#MaestroPaths.ts';
17
+ import { getWorkflowEventByBuilderHandoff } from '#workflow/builder/utils/getWorkflowEventByBuilderHandoff.ts';
18
18
  import { readWorkflowState } from '#workflow/state/readWorkflowState.ts';
19
- import {
20
- WORKFLOW_EVENTS,
21
- WORKFLOW_PHASES,
22
- type WorkflowState,
23
- } from '#workflow/state/schema.ts';
19
+ import { WORKFLOW_PHASES, type WorkflowState } from '#workflow/state/schema.ts';
24
20
  import { writeWorkflowState } from '#workflow/state/writeWorkflowState.ts';
25
21
  import { transitionWorkflow } from '#workflow/transitions.ts';
26
22
 
@@ -82,12 +78,7 @@ export const completeBuilderPass = async ({
82
78
 
83
79
  const nextState = transitionWorkflow({
84
80
  state: currentState,
85
- event:
86
- draftHandoff.status === BUILDER_HANDOFF_STATUSES.DONE
87
- ? WORKFLOW_EVENTS.BUILDER_DONE
88
- : draftHandoff.status === BUILDER_HANDOFF_STATUSES.ESCALATION
89
- ? WORKFLOW_EVENTS.OPEN_ESCALATION
90
- : WORKFLOW_EVENTS.BUILDER_FAILED,
81
+ event: getWorkflowEventByBuilderHandoff(handoff),
91
82
  });
92
83
 
93
84
  await mkdir(paths.getBuilderHandoffsPath(specId), { recursive: true });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Objective: Select the workflow event from a builder handoff.
3
+ * Used: When a builder completes a pass with done or escalation.
4
+ */
5
+
6
+ import {
7
+ BUILDER_HANDOFF_STATUSES,
8
+ type BuilderHandoff,
9
+ } from '#artifacts/builder-handoff/schema.ts';
10
+ import { WORKFLOW_EVENTS, type WorkflowEvent } from '#workflow/state/schema.ts';
11
+
12
+ export const getWorkflowEventByBuilderHandoff = ({
13
+ status,
14
+ }: BuilderHandoff): WorkflowEvent => {
15
+ if (status === BUILDER_HANDOFF_STATUSES.DONE) {
16
+ return WORKFLOW_EVENTS.BUILDER_DONE;
17
+ }
18
+
19
+ if (status === BUILDER_HANDOFF_STATUSES.ESCALATION) {
20
+ return WORKFLOW_EVENTS.OPEN_ESCALATION;
21
+ }
22
+
23
+ throw new Error(`Unexpected builder handoff status: "${status}".`);
24
+ };
@@ -14,7 +14,6 @@ export const WORKFLOW_PHASES = {
14
14
  READY_FOR_BUILDER: 'ready-for-builder',
15
15
  BUILDER_RUNNING: 'builder-running',
16
16
  ESCALATION_DECISION: 'escalation-decision',
17
- BUILDER_FAILED: 'builder-failed',
18
17
  READY_FOR_VERIFIER: 'ready-for-verifier',
19
18
  VERIFIER_RUNNING: 'verifier-running',
20
19
  FINDINGS_DECISION: 'findings-decision',
@@ -26,7 +25,6 @@ export const WORKFLOW_EVENTS = {
26
25
  RUN_BUILDER: 'run-builder',
27
26
  OPEN_ESCALATION: 'open-escalation',
28
27
  RESOLVE_ESCALATION: 'resolve-escalation',
29
- BUILDER_FAILED: 'builder-failed',
30
28
  BUILDER_DONE: 'builder-done',
31
29
  RUN_VERIFIER: 'run-verifier',
32
30
  VERIFIER_FOUND_FINDINGS: 'verifier-found-findings',
@@ -40,7 +38,6 @@ export const WorkflowPhaseSchema = Type.Union([
40
38
  Type.Literal(WORKFLOW_PHASES.READY_FOR_BUILDER),
41
39
  Type.Literal(WORKFLOW_PHASES.BUILDER_RUNNING),
42
40
  Type.Literal(WORKFLOW_PHASES.ESCALATION_DECISION),
43
- Type.Literal(WORKFLOW_PHASES.BUILDER_FAILED),
44
41
  Type.Literal(WORKFLOW_PHASES.READY_FOR_VERIFIER),
45
42
  Type.Literal(WORKFLOW_PHASES.VERIFIER_RUNNING),
46
43
  Type.Literal(WORKFLOW_PHASES.FINDINGS_DECISION),
@@ -38,10 +38,6 @@ export const WORKFLOW_TRANSITIONS: Record<WorkflowEvent, WorkflowTransition> = {
38
38
  from: [WORKFLOW_PHASES.ESCALATION_DECISION],
39
39
  to: WORKFLOW_PHASES.READY_FOR_BUILDER,
40
40
  },
41
- [WORKFLOW_EVENTS.BUILDER_FAILED]: {
42
- from: [WORKFLOW_PHASES.BUILDER_RUNNING],
43
- to: WORKFLOW_PHASES.BUILDER_FAILED,
44
- },
45
41
  [WORKFLOW_EVENTS.BUILDER_DONE]: {
46
42
  from: [WORKFLOW_PHASES.BUILDER_RUNNING],
47
43
  to: WORKFLOW_PHASES.READY_FOR_VERIFIER,
@@ -9,13 +9,10 @@ import type { VerifierHandoff } from '#artifacts/verifier-handoff/schema.ts';
9
9
  import { writeVerifierHandoff } from '#artifacts/verifier-handoff/writeVerifierHandoff.ts';
10
10
  import type { MaestroPaths } from '#MaestroPaths.ts';
11
11
  import { readWorkflowState } from '#workflow/state/readWorkflowState.ts';
12
- import {
13
- WORKFLOW_EVENTS,
14
- WORKFLOW_PHASES,
15
- type WorkflowState,
16
- } from '#workflow/state/schema.ts';
12
+ import { WORKFLOW_PHASES, type WorkflowState } from '#workflow/state/schema.ts';
17
13
  import { writeWorkflowState } from '#workflow/state/writeWorkflowState.ts';
18
14
  import { transitionWorkflow } from '#workflow/transitions.ts';
15
+ import { getWorkflowEventByVerifierHandoff } from '#workflow/verifier/utils/getWorkflowEventByVerifierHandoff.ts';
19
16
 
20
17
  export type CompletedVerifierPass = {
21
18
  handoff: VerifierHandoff;
@@ -61,10 +58,7 @@ export const completeVerifierPass = async ({
61
58
 
62
59
  const nextState = transitionWorkflow({
63
60
  state: currentState,
64
- event:
65
- handoff.findings.length > 0
66
- ? WORKFLOW_EVENTS.VERIFIER_FOUND_FINDINGS
67
- : WORKFLOW_EVENTS.VERIFIER_APPROVED,
61
+ event: getWorkflowEventByVerifierHandoff(handoff),
68
62
  });
69
63
 
70
64
  await mkdir(paths.getVerifierHandoffsPath(specId), { recursive: true });
@@ -13,13 +13,10 @@ import { FINDING_DECISIONS } from '#artifacts/verifier-handoff/schema.ts';
13
13
  import type { MaestroPaths } from '#MaestroPaths.ts';
14
14
  import { writeJson } from '#utils/write-json.ts';
15
15
  import { readWorkflowState } from '#workflow/state/readWorkflowState.ts';
16
- import {
17
- WORKFLOW_EVENTS,
18
- WORKFLOW_PHASES,
19
- type WorkflowState,
20
- } from '#workflow/state/schema.ts';
16
+ import { WORKFLOW_PHASES, type WorkflowState } from '#workflow/state/schema.ts';
21
17
  import { writeWorkflowState } from '#workflow/state/writeWorkflowState.ts';
22
18
  import { transitionWorkflow } from '#workflow/transitions.ts';
19
+ import { getWorkflowEventByVerifierHandoff } from '#workflow/verifier/utils/getWorkflowEventByVerifierHandoff.ts';
23
20
 
24
21
  type FindingDecisionRejected = {
25
22
  findingId: string;
@@ -159,18 +156,13 @@ const buildFindingWorkflow = ({
159
156
  return { ...finding, decision: { decision: FINDING_DECISIONS.FIX_CODE } };
160
157
  });
161
158
 
162
- const isFixCodeRequested = decisions.some(
163
- (decision) => decision.decision === FINDING_DECISIONS.FIX_CODE,
164
- );
159
+ const nextHandoff = { ...handoff, findings };
165
160
 
166
161
  const nextState = transitionWorkflow({
167
162
  state,
168
- event: isFixCodeRequested
169
- ? WORKFLOW_EVENTS.REQUEST_FIXES
170
- : WORKFLOW_EVENTS.REJECT_FINDINGS,
163
+ event: getWorkflowEventByVerifierHandoff(nextHandoff),
171
164
  });
172
165
 
173
- const nextHandoff = { ...handoff, findings };
174
166
  assertVerifierHandoff({
175
167
  handoff: nextHandoff,
176
168
  specId: state.specId,
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Objective: Select the workflow event from a verifier handoff.
3
+ * Used: When a verifier completes a pass or the owner resolves its findings.
4
+ */
5
+
6
+ import {
7
+ FINDING_DECISIONS,
8
+ type VerifierHandoff,
9
+ } from '#artifacts/verifier-handoff/schema.ts';
10
+ import { WORKFLOW_EVENTS, type WorkflowEvent } from '#workflow/state/schema.ts';
11
+
12
+ export const getWorkflowEventByVerifierHandoff = (
13
+ handoff: VerifierHandoff,
14
+ ): WorkflowEvent => {
15
+ if (handoff.findings.length === 0) {
16
+ return WORKFLOW_EVENTS.VERIFIER_APPROVED;
17
+ }
18
+
19
+ if (handoff.findings.every(({ decision }) => decision === null)) {
20
+ return WORKFLOW_EVENTS.VERIFIER_FOUND_FINDINGS;
21
+ }
22
+
23
+ if (
24
+ handoff.findings.some(
25
+ ({ decision }) => decision?.decision === FINDING_DECISIONS.FIX_CODE,
26
+ )
27
+ ) {
28
+ return WORKFLOW_EVENTS.REQUEST_FIXES;
29
+ }
30
+
31
+ if (
32
+ handoff.findings.every(
33
+ ({ decision }) => decision?.decision === FINDING_DECISIONS.REJECT,
34
+ )
35
+ ) {
36
+ return WORKFLOW_EVENTS.REJECT_FINDINGS;
37
+ }
38
+
39
+ throw new Error('Unexpected verifier finding decisions.');
40
+ };