@emiliosp/pi-maestro 0.8.0 → 0.9.0
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 +31 -0
- package/CHANGELOG.md +6 -0
- package/README.md +2 -1
- package/ROADMAP.md +3 -2
- package/agents/builder.md +2 -4
- package/docs/glossary.md +1 -1
- package/docs/workflow.md +11 -8
- package/package.json +1 -1
- package/src/artifacts/builder-handoff/assertBuilderHandoff.ts +0 -14
- package/src/artifacts/builder-handoff/schema.ts +0 -26
- package/src/maestro/instructions/getMaestroInstructions.ts +4 -2
- package/src/maestro/status/formatMaestroStatus.ts +0 -3
- package/src/tools/child/record-builder-handoff.ts +2 -10
- package/src/tools/main/resolve-escalations.ts +1 -1
- package/src/tools/main/resolve-findings.ts +1 -1
- package/src/tools/main/run-builder.ts +9 -49
- package/src/workflow/builder/completeBuilderPass.ts +4 -13
- package/src/workflow/builder/utils/getWorkflowEventByBuilderHandoff.ts +24 -0
- package/src/workflow/state/schema.ts +0 -3
- package/src/workflow/transitions.ts +0 -4
- package/src/workflow/verifier/completeVerifierPass.ts +3 -9
- package/src/workflow/{findings → verifier}/resolveFindings.ts +4 -12
- package/src/workflow/verifier/utils/getWorkflowEventByVerifierHandoff.ts +40 -0
- /package/src/workflow/{escalation → builder}/resolveEscalations.ts +0 -0
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,12 @@
|
|
|
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
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
1. Breaking: `builder` reports now support only `done` and `escalation`. Reportable technical problems use the existing `owner` `escalation` process.
|
|
8
|
+
2. Removed the terminal `builder` failure phase and event. Execution errors and missing or invalid saved results still stop the workflow without automatic recovery.
|
|
9
|
+
3. No migrations, compatibility readers, or deprecated aliases are provided. `Artifact` versions remain `1.0.0`.
|
|
10
|
+
|
|
5
11
|
## 0.7.3
|
|
6
12
|
|
|
7
13
|
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.
|
|
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
|
@@ -46,9 +46,10 @@ Renamed `test/` to `tests/` at the owner's request. Updated imports, fixture pat
|
|
|
46
46
|
|
|
47
47
|
#### Objective
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
The `builder` reports technical problems that prevent completion through the existing `escalation` process. The `owner` decides how to proceed.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
The approved [specification](.specs/20261013-101454-route-builder-failures-through-owner-escalations/spec.md) is implemented.
|
|
52
|
+
Automatic recovery of interrupted `runs` remains outside this item.
|
|
52
53
|
|
|
53
54
|
#### Completed when
|
|
54
55
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
package/package.json
CHANGED
|
@@ -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<
|
|
@@ -83,8 +83,8 @@ Read the returned artifact and follow its outcome:
|
|
|
83
83
|
|
|
84
84
|
1. done: The phase is ready-for-verifier. Call maestro_run_verifier with the active specId.
|
|
85
85
|
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
86
|
|
|
87
|
+
Reportable technical problems that prevent builder completion use the existing escalation process. The owner decides how work continues.
|
|
88
88
|
A significant discovery needs an escalation when the owner must choose between meaningful alternatives, even without a technical blocker.
|
|
89
89
|
Do not invent an escalation for routine implementation details. Read significant discoveries without owner decisions from the builder handoff notes.
|
|
90
90
|
|
|
@@ -142,9 +142,11 @@ If a tool fails, inspect its error, workflow.json, and artifacts before taking a
|
|
|
142
142
|
If the tool wrote no artifacts or transition, correct recoverable errors within your permissions before retrying.
|
|
143
143
|
If it partially updated artifacts or workflow state, report that state and stop. Do not retry a partially completed transition.
|
|
144
144
|
Do not repeat an unchanged failing call, edit protocol files, or fabricate evidence to bypass an error.
|
|
145
|
+
Launch errors, delegation failures, timeouts, interruptions, cancellations, and missing or invalid handoffs stop the workflow. Do not create synthetic escalations or recovery transitions.
|
|
145
146
|
If a run ends without a valid saved result, report the error and stop. Do not repair product files or force a transition.
|
|
147
|
+
If a run saves no completed transition, its saved running phase remains unchanged.
|
|
146
148
|
Disabling Maestro, restarting Pi, or using /resume clears live state. Do not reconstruct or resume an incomplete workflow.
|
|
147
|
-
The owner handles
|
|
149
|
+
The owner handles execution errors, protocol errors, and interrupted workflows manually.
|
|
148
150
|
|
|
149
151
|
At candidate-ready, the workflow is complete. No final tool call is required.
|
|
150
152
|
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
|
|
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
|
|
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/
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 "${
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
+
};
|
|
File without changes
|