@atolis-hq/wake 0.2.56 → 0.2.58

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;
@@ -334,38 +334,62 @@ export function createTickRunner(deps) {
334
334
  nextStage: null,
335
335
  };
336
336
  }
337
+ // GitHub rejects a review that would approve the PR's own author (422 "Can
338
+ // not approve your own pull request") — the default single-identity setup
339
+ // where implement and pr-review share one bot account. Permanent, not
340
+ // transient: never worth retrying, so it's treated as a policy block
341
+ // rather than left to propagate as an uncaught tick failure.
342
+ function isSelfApprovalError(error) {
343
+ return (error instanceof Error &&
344
+ error.status === 422 &&
345
+ error.message.toLowerCase().includes('approve your own pull request'));
346
+ }
337
347
  async function performApprovedPrMergeActions(input) {
338
348
  if (deps.prMergeActor === undefined ||
339
349
  input.approvalResolution.targetResourceUri === undefined ||
340
350
  input.approvalResolution.triggeringCommentId === undefined) {
341
- return;
351
+ return { blocked: false };
342
352
  }
343
353
  const targetResourceUri = input.approvalResolution.targetResourceUri;
344
354
  const commentId = input.approvalResolution.triggeringCommentId.replace(/[^a-z0-9]+/gi, '-');
345
355
  const reviewBody = reviewerMessageFromApprovalComment(input.approvalResolution.triggeringCommentBody ?? '');
356
+ let blockedReason = null;
346
357
  const approvedEventId = `pr-merge-approved-${commentId}`;
347
358
  if (input.mergePolicy.approve &&
348
359
  (await deps.stateStore.readEventEnvelope(approvedEventId)) === null) {
349
- await deps.prMergeActor.approve(targetResourceUri, reviewBody);
350
- const occurredAt = eventStampNow();
351
- await deps.stateStore.appendEventEnvelope(createEventEnvelope({
352
- eventId: approvedEventId,
353
- workItemKey: input.projection.workItemKey,
354
- streamScope: 'work-item',
355
- direction: 'internal',
356
- sourceSystem: 'wake',
357
- sourceEventType: PR_REVIEW_APPROVED_EVENT,
358
- sourceRefs: {
359
- resourceUri: targetResourceUri,
360
- commentId: input.approvalResolution.triggeringCommentId,
361
- },
362
- occurredAt,
363
- ingestedAt: occurredAt,
364
- trigger: 'context-only',
365
- payload: {
366
- idempotencyKey: `${input.approvalResolution.triggeringCommentId}:pr-review-approval`,
367
- },
368
- }));
360
+ let approved = true;
361
+ try {
362
+ await deps.prMergeActor.approve(targetResourceUri, reviewBody);
363
+ }
364
+ catch (error) {
365
+ if (!isSelfApprovalError(error)) {
366
+ throw error;
367
+ }
368
+ approved = false;
369
+ blockedReason =
370
+ 'Merge policy blocked the approval step because GitHub refuses a review that approves its own author (implement and pr-review run as the same bot identity). Either disable merge.approve and rely on merge.autoMerge alone, or configure a second reviewer identity.';
371
+ }
372
+ if (approved) {
373
+ const occurredAt = eventStampNow();
374
+ await deps.stateStore.appendEventEnvelope(createEventEnvelope({
375
+ eventId: approvedEventId,
376
+ workItemKey: input.projection.workItemKey,
377
+ streamScope: 'work-item',
378
+ direction: 'internal',
379
+ sourceSystem: 'wake',
380
+ sourceEventType: PR_REVIEW_APPROVED_EVENT,
381
+ sourceRefs: {
382
+ resourceUri: targetResourceUri,
383
+ commentId: input.approvalResolution.triggeringCommentId,
384
+ },
385
+ occurredAt,
386
+ ingestedAt: occurredAt,
387
+ trigger: 'context-only',
388
+ payload: {
389
+ idempotencyKey: `${input.approvalResolution.triggeringCommentId}:pr-review-approval`,
390
+ },
391
+ }));
392
+ }
369
393
  }
370
394
  const autoMergeEventId = `pr-auto-merge-enabled-${commentId}`;
371
395
  if (input.mergePolicy.autoMerge &&
@@ -391,6 +415,10 @@ export function createTickRunner(deps) {
391
415
  },
392
416
  }));
393
417
  }
418
+ if (blockedReason !== null) {
419
+ return { blocked: true, reason: blockedReason };
420
+ }
421
+ return { blocked: false };
394
422
  }
395
423
  // The one deterministic approval transition: /approved, wake:auto, and a
396
424
  // watcher child's onSuccess.approve all resolve a pending approval through
@@ -1031,7 +1059,20 @@ export function createTickRunner(deps) {
1031
1059
  action = entryStage.action ?? entryStageName;
1032
1060
  claimedStage = entryStageName;
1033
1061
  workspaceMode = entryStage.workspace;
1034
- promptContextOverrides = entryStage.promptContext;
1062
+ // The triggering event's own body (e.g. a refine plan comment) is
1063
+ // durable and already local the moment this fires — projection.comments
1064
+ // only gets it once a later GitHub poll echoes it back, which a watcher
1065
+ // dispatched off the same-tick completion event can easily outrace.
1066
+ const triggeringEvent = watcherDispatch.trigger.kind === 'event'
1067
+ ? await deps.stateStore.readEventEnvelope(watcherDispatch.trigger.eventId)
1068
+ : null;
1069
+ const triggeringBody = triggeringEvent?.payload.body;
1070
+ promptContextOverrides = {
1071
+ ...entryStage.promptContext,
1072
+ ...(typeof triggeringBody === 'string'
1073
+ ? { parentPendingReviewBody: triggeringBody }
1074
+ : {}),
1075
+ };
1035
1076
  workflowName = watcherDispatch.targetWorkflowName;
1036
1077
  watcherStateKeyForRun = watcherKey({
1037
1078
  workItemKey: watcherDispatch.projection.workItemKey,
@@ -1078,11 +1119,19 @@ export function createTickRunner(deps) {
1078
1119
  workflowName,
1079
1120
  });
1080
1121
  }
1081
- await performApprovedPrMergeActions({
1122
+ const mergeActionsResult = await performApprovedPrMergeActions({
1082
1123
  projection: candidate,
1083
1124
  approvalResolution,
1084
1125
  mergePolicy,
1085
1126
  });
1127
+ if (mergeActionsResult.blocked) {
1128
+ return await publishPrMergePolicyBlock({
1129
+ projection: candidate,
1130
+ approvalResolution,
1131
+ reason: mergeActionsResult.reason,
1132
+ workflowName,
1133
+ });
1134
+ }
1086
1135
  }
1087
1136
  const approvalId = `approval-${candidate.issue.number}-${deps.clock.now().getTime()}`;
1088
1137
  const approvedAt = deps.clock.now().toISOString();
@@ -124,4 +124,4 @@ export function resolveWakeVersion(options = {}) {
124
124
  }
125
125
  return '0.1.0-dev';
126
126
  }
127
- export const wakeVersion = "g38b681f";
127
+ export const wakeVersion = "g4624f39";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atolis-hq/wake",
3
- "version": "0.2.56",
3
+ "version": "0.2.58",
4
4
  "description": "Local autonomous agent control plane for software development",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -9,6 +9,15 @@ You are Wake, in the PLAN-REVIEW workflow for work item {{workItemKey}}.
9
9
 
10
10
  Your job is only to determine whether the pending plan on this work item is ready to proceed to the next stage.
11
11
 
12
+ The pending plan under review:
13
+ <wake-pending-plan>
14
+ {{#if parentPendingReviewBody}}
15
+ {{parentPendingReviewBody}}
16
+ {{else}}
17
+ (Wake did not attach the pending plan text to this run. Fall back to `gh issue view {{issueNumber}}` to read it directly — do not assume no plan exists just because it isn't shown above.)
18
+ {{/if}}
19
+ </wake-pending-plan>
20
+
12
21
  Assess whether it is safe and correct to approve as-is, letting Wake proceed unattended. Weigh:
13
22
  - Does the proposed plan actually address the issue as written, without silently narrowing, widening, or misreading the scope?
14
23
  - 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)?
@@ -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/*), Bash(gh api repos/*/commits/*), Bash(git status), Bash(git log *), Bash(git diff *), Read, Glob, Grep
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
- - Identify the pull request for this work item using read-only GitHub commands.
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.