@atolis-hq/wake 0.2.55 → 0.2.57
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.
|
@@ -168,6 +168,10 @@ export async function buildStagePrompt(input) {
|
|
|
168
168
|
if (resolvedWorkspaceMode === 'branch') {
|
|
169
169
|
context.branch = branchNameForIssue(input.projection.issue.number);
|
|
170
170
|
}
|
|
171
|
+
// Opaque to this file (resourceUri locator grammar is provider-specific,
|
|
172
|
+
// ADR 0001 §1) — passed through as-is so a provider-aware prompt template
|
|
173
|
+
// can interpret it without stage-prompt.ts knowing any adapter's format.
|
|
174
|
+
context.correlatedResources = input.projection.correlatedResources;
|
|
171
175
|
const allowedTools = parseFrontmatterList(template.frontmatter.allowedTools);
|
|
172
176
|
const allowedToolsListStr = allowedTools.length > 0 ? allowedTools.join(', ') : '(none)';
|
|
173
177
|
context.allowedToolsList = allowedToolsListStr;
|
|
@@ -195,8 +195,10 @@ export function createTickRunner(deps) {
|
|
|
195
195
|
return projection.issue.labels.includes(label);
|
|
196
196
|
}
|
|
197
197
|
function approvedMergePolicyForStage(stage) {
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
// Schema defaults materialize a disabled merge block whenever onSuccess is
|
|
199
|
+
// set (e.g. approve-only watchers); treat it as no merge policy at all.
|
|
200
|
+
const merge = stage?.watch?.find((watch) => watch.onSuccess?.merge !== undefined)?.onSuccess?.merge ?? null;
|
|
201
|
+
return merge !== null && (merge.approve || merge.autoMerge) ? merge : null;
|
|
200
202
|
}
|
|
201
203
|
function escapeRegex(input) {
|
|
202
204
|
return input.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -390,6 +392,50 @@ export function createTickRunner(deps) {
|
|
|
390
392
|
}));
|
|
391
393
|
}
|
|
392
394
|
}
|
|
395
|
+
// The one deterministic approval transition: /approved, wake:auto, and a
|
|
396
|
+
// watcher child's onSuccess.approve all resolve a pending approval through
|
|
397
|
+
// this same event shape, so replay folds them identically.
|
|
398
|
+
async function applyApprovalTransition(input) {
|
|
399
|
+
const nextStage = lifecycle.nextStageFromSentinel(input.projection.wake.stage, 'DONE', input.workflow);
|
|
400
|
+
if (nextStage === null) {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
const approvalCompletedEvent = createEventEnvelope({
|
|
404
|
+
eventId: `${input.approvalId}-completed`,
|
|
405
|
+
workItemKey: input.projection.workItemKey,
|
|
406
|
+
streamScope: 'work-item',
|
|
407
|
+
direction: 'internal',
|
|
408
|
+
sourceSystem: 'wake',
|
|
409
|
+
sourceEventType: RUN_COMPLETED_EVENT,
|
|
410
|
+
sourceRefs: {
|
|
411
|
+
repo: input.projection.issue.repo,
|
|
412
|
+
issueNumber: input.projection.issue.number,
|
|
413
|
+
runId: input.approvalId,
|
|
414
|
+
},
|
|
415
|
+
occurredAt: input.approvedAt,
|
|
416
|
+
ingestedAt: input.approvedAt,
|
|
417
|
+
trigger: 'immediate',
|
|
418
|
+
payload: {
|
|
419
|
+
action: input.pendingAction,
|
|
420
|
+
sentinel: 'DONE',
|
|
421
|
+
nextStage,
|
|
422
|
+
runId: input.approvalId,
|
|
423
|
+
reason: input.reason,
|
|
424
|
+
...input.payloadExtras,
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
await deps.stateStore.appendEventEnvelope(approvalCompletedEvent);
|
|
428
|
+
await projectionUpdater.rebuildFromEvents([approvalCompletedEvent]);
|
|
429
|
+
await deliverOutboundEvent(createLabelsEvent({
|
|
430
|
+
projection: input.projection,
|
|
431
|
+
runId: input.approvalId,
|
|
432
|
+
statusLabel: statusLabelForStage(nextStage),
|
|
433
|
+
stageLabel: stageLabelForStage(nextStage),
|
|
434
|
+
workflowLabel: workflowLabelForWorkflowName(input.workflowName),
|
|
435
|
+
occurredAt: input.approvedAt,
|
|
436
|
+
}));
|
|
437
|
+
return { nextStage };
|
|
438
|
+
}
|
|
393
439
|
// Closes the loop on #82's review feedback: rather than scrape a PR link
|
|
394
440
|
// out of the agent's free text, the agent emits a `wake-artifacts` fence
|
|
395
441
|
// (domain/schema.ts's parseRunnerArtifacts) and Wake verifies each claim
|
|
@@ -1041,48 +1087,29 @@ export function createTickRunner(deps) {
|
|
|
1041
1087
|
const approvalId = `approval-${candidate.issue.number}-${deps.clock.now().getTime()}`;
|
|
1042
1088
|
const approvedAt = deps.clock.now().toISOString();
|
|
1043
1089
|
const automaticApproval = approvalResolution.automatic === true;
|
|
1044
|
-
const
|
|
1045
|
-
|
|
1090
|
+
const applied = await applyApprovalTransition({
|
|
1091
|
+
projection: candidate,
|
|
1092
|
+
pendingAction: approvalResolution.pendingAction,
|
|
1093
|
+
approvalId,
|
|
1094
|
+
approvedAt,
|
|
1095
|
+
reason: automaticApproval ? 'auto:approved' : 'human:approved',
|
|
1096
|
+
workflow,
|
|
1097
|
+
workflowName,
|
|
1098
|
+
payloadExtras: automaticApproval
|
|
1099
|
+
? {
|
|
1100
|
+
autoResolution: {
|
|
1101
|
+
kind: 'awaiting-approval',
|
|
1102
|
+
classification: 'auto-approval',
|
|
1103
|
+
reasoning: approvalResolution.reason,
|
|
1104
|
+
},
|
|
1105
|
+
}
|
|
1106
|
+
: {
|
|
1107
|
+
handledCommentId: approvalResolution.triggeringCommentId ?? latestHumanCommentId(candidate),
|
|
1108
|
+
},
|
|
1109
|
+
});
|
|
1110
|
+
if (applied === null) {
|
|
1046
1111
|
return { status: 'idle' };
|
|
1047
1112
|
}
|
|
1048
|
-
const approvalCompletedEvent = createEventEnvelope({
|
|
1049
|
-
eventId: `${approvalId}-completed`,
|
|
1050
|
-
workItemKey: candidate.workItemKey,
|
|
1051
|
-
streamScope: 'work-item',
|
|
1052
|
-
direction: 'internal',
|
|
1053
|
-
sourceSystem: 'wake',
|
|
1054
|
-
sourceEventType: RUN_COMPLETED_EVENT,
|
|
1055
|
-
sourceRefs: {
|
|
1056
|
-
repo: candidate.issue.repo,
|
|
1057
|
-
issueNumber: candidate.issue.number,
|
|
1058
|
-
runId: approvalId,
|
|
1059
|
-
},
|
|
1060
|
-
occurredAt: approvedAt,
|
|
1061
|
-
ingestedAt: approvedAt,
|
|
1062
|
-
trigger: 'immediate',
|
|
1063
|
-
payload: {
|
|
1064
|
-
action: approvalResolution.pendingAction,
|
|
1065
|
-
sentinel: 'DONE',
|
|
1066
|
-
nextStage,
|
|
1067
|
-
runId: approvalId,
|
|
1068
|
-
reason: automaticApproval ? 'auto:approved' : 'human:approved',
|
|
1069
|
-
...(automaticApproval
|
|
1070
|
-
? {}
|
|
1071
|
-
: {
|
|
1072
|
-
handledCommentId: approvalResolution.triggeringCommentId ?? latestHumanCommentId(candidate),
|
|
1073
|
-
}),
|
|
1074
|
-
...(automaticApproval
|
|
1075
|
-
? {
|
|
1076
|
-
autoResolution: {
|
|
1077
|
-
kind: 'awaiting-approval',
|
|
1078
|
-
classification: 'auto-approval',
|
|
1079
|
-
reasoning: approvalResolution.reason,
|
|
1080
|
-
},
|
|
1081
|
-
}
|
|
1082
|
-
: {}),
|
|
1083
|
-
},
|
|
1084
|
-
});
|
|
1085
|
-
await deps.stateStore.appendEventEnvelope(approvalCompletedEvent);
|
|
1086
1113
|
if (automaticApproval) {
|
|
1087
1114
|
await appendAuditEvent({
|
|
1088
1115
|
eventId: `${approvalId}-audit-auto-resolution`,
|
|
@@ -1102,7 +1129,7 @@ export function createTickRunner(deps) {
|
|
|
1102
1129
|
},
|
|
1103
1130
|
outcome: {
|
|
1104
1131
|
approved: true,
|
|
1105
|
-
nextStage,
|
|
1132
|
+
nextStage: applied.nextStage,
|
|
1106
1133
|
reason: approvalResolution.reason,
|
|
1107
1134
|
},
|
|
1108
1135
|
timestamp: approvedAt,
|
|
@@ -1112,20 +1139,11 @@ export function createTickRunner(deps) {
|
|
|
1112
1139
|
},
|
|
1113
1140
|
});
|
|
1114
1141
|
}
|
|
1115
|
-
await projectionUpdater.rebuildFromEvents([approvalCompletedEvent]);
|
|
1116
|
-
await deliverOutboundEvent(createLabelsEvent({
|
|
1117
|
-
projection: candidate,
|
|
1118
|
-
runId: approvalId,
|
|
1119
|
-
statusLabel: statusLabelForStage(nextStage),
|
|
1120
|
-
stageLabel: stageLabelForStage(nextStage),
|
|
1121
|
-
workflowLabel: workflowLabelForWorkflowName(workflowName),
|
|
1122
|
-
occurredAt: approvedAt,
|
|
1123
|
-
}));
|
|
1124
1142
|
return {
|
|
1125
1143
|
status: 'processed',
|
|
1126
1144
|
runId: approvalId,
|
|
1127
1145
|
sentinel: 'DONE',
|
|
1128
|
-
nextStage,
|
|
1146
|
+
nextStage: applied.nextStage,
|
|
1129
1147
|
};
|
|
1130
1148
|
}
|
|
1131
1149
|
else {
|
|
@@ -1733,6 +1751,10 @@ export function createTickRunner(deps) {
|
|
|
1733
1751
|
if (watcherRun) {
|
|
1734
1752
|
// Watcher-dispatched runs own PR verdict delivery and correlation
|
|
1735
1753
|
// registration regardless of the target workflow/action name.
|
|
1754
|
+
const pendingApprovalAction = candidate.context.pendingApprovalAction;
|
|
1755
|
+
const watcherSuccessPolicy = watcherDispatch === null
|
|
1756
|
+
? undefined
|
|
1757
|
+
: deps.config.workflows[watcherDispatch.parentWorkflowName]?.stages[watcherDispatch.parentStage]?.watch?.[watcherDispatch.watcherIndex]?.onSuccess;
|
|
1736
1758
|
if (prReviewTargetResourceUri !== null &&
|
|
1737
1759
|
(sentinel === 'DONE' || sentinel === 'FAILED')) {
|
|
1738
1760
|
await deliverOutboundEvent({
|
|
@@ -1751,6 +1773,58 @@ export function createTickRunner(deps) {
|
|
|
1751
1773
|
},
|
|
1752
1774
|
});
|
|
1753
1775
|
}
|
|
1776
|
+
else if (watcherDispatch !== null &&
|
|
1777
|
+
watcherSuccessPolicy?.approve === true &&
|
|
1778
|
+
(sentinel === 'DONE' || sentinel === 'FAILED' || sentinel === 'BLOCKED')) {
|
|
1779
|
+
// No PR surface to carry the verdict comment: the child's sentinel
|
|
1780
|
+
// is its verdict. Publish the review body for every verdict so the
|
|
1781
|
+
// human sees why; only DONE resolves the parent's pending gate.
|
|
1782
|
+
await deliverOutboundEvent(publishIntent);
|
|
1783
|
+
const approvalId = `${runId}-parent-approval`;
|
|
1784
|
+
const parentWorkflow = deps.config.workflows[watcherDispatch.parentWorkflowName];
|
|
1785
|
+
if (sentinel === 'DONE' &&
|
|
1786
|
+
isAwaitingApproval(candidate) &&
|
|
1787
|
+
typeof pendingApprovalAction === 'string' &&
|
|
1788
|
+
parentWorkflow !== undefined &&
|
|
1789
|
+
(await deps.stateStore.readEventEnvelope(`${approvalId}-completed`)) === null) {
|
|
1790
|
+
const approvedAt = eventStampNow();
|
|
1791
|
+
const applied = await applyApprovalTransition({
|
|
1792
|
+
projection: candidate,
|
|
1793
|
+
pendingAction: pendingApprovalAction,
|
|
1794
|
+
approvalId,
|
|
1795
|
+
approvedAt,
|
|
1796
|
+
reason: 'watcher:approved',
|
|
1797
|
+
workflow: parentWorkflow,
|
|
1798
|
+
workflowName: watcherDispatch.parentWorkflowName,
|
|
1799
|
+
});
|
|
1800
|
+
if (applied !== null) {
|
|
1801
|
+
await appendAuditEvent({
|
|
1802
|
+
eventId: `${approvalId}-audit-watcher-approval`,
|
|
1803
|
+
decisionType: 'approval.watcher-resolved',
|
|
1804
|
+
workItemKey: candidate.workItemKey,
|
|
1805
|
+
runId,
|
|
1806
|
+
workflowRevision: await computeWorkflowRevision({
|
|
1807
|
+
config: deps.config,
|
|
1808
|
+
workflowName: watcherDispatch.parentWorkflowName,
|
|
1809
|
+
workflow: parentWorkflow,
|
|
1810
|
+
action: pendingApprovalAction,
|
|
1811
|
+
}),
|
|
1812
|
+
inputsConsidered: {
|
|
1813
|
+
watcherWorkflow: watcherDispatch.targetWorkflowName,
|
|
1814
|
+
pendingAction: pendingApprovalAction,
|
|
1815
|
+
childRunId: runId,
|
|
1816
|
+
childSentinel: sentinel,
|
|
1817
|
+
},
|
|
1818
|
+
outcome: { approved: true, nextStage: applied.nextStage },
|
|
1819
|
+
timestamp: approvedAt,
|
|
1820
|
+
sourceRefs: {
|
|
1821
|
+
repo: candidate.issue.repo,
|
|
1822
|
+
issueNumber: candidate.issue.number,
|
|
1823
|
+
},
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1754
1828
|
else {
|
|
1755
1829
|
await suppressOutboundEvent(publishIntent, {
|
|
1756
1830
|
suppressedPublishReason: 'pr-review-no-actionable-verdict',
|
|
@@ -430,6 +430,12 @@ const approvedMergePolicySchema = z
|
|
|
430
430
|
blockedPaths: [],
|
|
431
431
|
blockedLabels: [],
|
|
432
432
|
});
|
|
433
|
+
const watchSuccessPolicySchema = z.object({
|
|
434
|
+
// Resolve the watched parent stage's pending approval gate when the child
|
|
435
|
+
// workflow run completes DONE — the child's sentinel is its verdict.
|
|
436
|
+
approve: z.boolean().default(false),
|
|
437
|
+
merge: approvedMergePolicySchema.optional(),
|
|
438
|
+
});
|
|
433
439
|
const workflowStageSchema = stageRouteSchema.extend({
|
|
434
440
|
workspace: workflowWorkspaceSchema,
|
|
435
441
|
onDone: identifierSchema,
|
|
@@ -445,11 +451,7 @@ const workflowStageSchema = stageRouteSchema.extend({
|
|
|
445
451
|
.optional(),
|
|
446
452
|
schedule: workflowTriggerScheduleSchema.optional(),
|
|
447
453
|
workflow: identifierSchema,
|
|
448
|
-
|
|
449
|
-
.object({
|
|
450
|
-
merge: approvedMergePolicySchema.optional(),
|
|
451
|
-
})
|
|
452
|
-
.optional(),
|
|
454
|
+
onSuccess: watchSuccessPolicySchema.optional(),
|
|
453
455
|
}))
|
|
454
456
|
.optional(),
|
|
455
457
|
});
|
package/dist/src/version.js
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
permissionMode: default
|
|
3
|
+
allowedTools: Read, Glob, Grep, Bash(git fetch), Bash(git status), Bash(gh issue view *), Bash(gh api repos/*/issues/*), WebSearch, WebFetch
|
|
4
|
+
maxTurns: 8
|
|
5
|
+
skipApproval: true
|
|
6
|
+
---
|
|
7
|
+
You are Wake, in the PLAN-REVIEW workflow for work item {{workItemKey}}.
|
|
8
|
+
{{toolCapabilityNote}}
|
|
9
|
+
|
|
10
|
+
Your job is only to determine whether the pending plan on this work item is ready to proceed to the next stage.
|
|
11
|
+
|
|
12
|
+
Assess whether it is safe and correct to approve as-is, letting Wake proceed unattended. Weigh:
|
|
13
|
+
- Does the proposed plan actually address the issue as written, without silently narrowing, widening, or misreading the scope?
|
|
14
|
+
- Are there open questions in Wake's comment that were never actually answered (a refine pass sometimes states assumptions instead of asking — treat unstated but load-bearing assumptions the same as open questions)?
|
|
15
|
+
- Does anything look unsafe to let proceed unattended (touches security-sensitive paths, proposes skipping tests/validation, makes an irreversible-sounding decision)?
|
|
16
|
+
- Are the architectural choices sound and aligned with the repo's long-term direction?
|
|
17
|
+
- Is this the kind of decision the operator would obviously make the same way every time, or does it need their specific judgment?
|
|
18
|
+
- Are there obvious better solutions that were not considered?
|
|
19
|
+
|
|
20
|
+
Write your assessment as your response body — it is posted to the issue for the record. Do not post comments yourself, and do not use `/approved` or `/changes` commands: Wake applies the outcome from your verdict.
|
|
21
|
+
|
|
22
|
+
Verdict mapping:
|
|
23
|
+
- Use `DONE` only when you are confident the plan should be approved; Wake resolves the pending approval and advances the stage.
|
|
24
|
+
- Use `FAILED` when the plan needs changes; explain the required changes clearly.
|
|
25
|
+
- Use `BLOCKED` when the decision needs human judgment.
|
|
26
|
+
|
|
27
|
+
{{feedbackCommandNote}}
|
package/prompts/pr-review.md
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
permissionMode: default
|
|
3
|
-
allowedTools: Bash(gh pr view *), Bash(gh pr diff *), Bash(gh pr checks *), Bash(gh run view *), Bash(gh api repos/*/pulls
|
|
3
|
+
allowedTools: Bash(gh pr view *), Bash(gh pr diff *), Bash(gh pr checks *), Bash(gh pr list *), Bash(gh run view *), Bash(gh api repos/*/pulls*), Bash(gh api repos/*/commits/*), Bash(gh issue view *), Bash(git status), Bash(git log *), Bash(git diff *), Read, Glob, Grep
|
|
4
4
|
maxTurns: 8
|
|
5
5
|
skipApproval: true
|
|
6
6
|
---
|
|
7
7
|
You are Wake, in the PR-REVIEW workflow for work item {{workItemKey}}.
|
|
8
8
|
|
|
9
|
+
Wake's known correlated resources for this work item (each has a `resourceUri` in `<provider>:<kind>:<locator>` form, e.g. a GitHub PR is `github:pr:<repo>#<number>`):
|
|
10
|
+
```
|
|
11
|
+
{{correlatedResources}}
|
|
12
|
+
```
|
|
13
|
+
|
|
9
14
|
Objective:
|
|
10
|
-
-
|
|
15
|
+
- If a resource above has `role: "implementation"` and `relation: "primary"`, that is the PR for this work item — use its `resourceUri` to identify the PR number (do not use the issue number) and review that PR. Do not substitute or guess a different PR.
|
|
16
|
+
- Otherwise, identify the pull request for this work item using read-only GitHub commands (`gh pr list`, `gh api repos/*/pulls`, `gh issue view {{issueNumber}}` to find a linked PR). Never assume the PR number equals the issue number — verify it. If you cannot find exactly one plausible PR, report `BLOCKED` rather than guess.
|
|
11
17
|
- Review the PR's diff, tests/checks, and surrounding code for correctness.
|
|
12
18
|
- Report the PR you examined using a `wake-artifacts` block.
|
|
13
19
|
- End with the Wake result envelope.
|