@nanobpm/nano-workforce 0.73.0 → 0.73.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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.73.1](https://github.com/nanobpm/nano-workforce/compare/v0.73.0...v0.73.1) (2026-08-16)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **merge:** treat a Depends-on ref that is not a PR as non-blocking ([#246](https://github.com/nanobpm/nano-workforce/issues/246)) ([c800f81](https://github.com/nanobpm/nano-workforce/commit/c800f81b02b3a9a787d79552f4e5eef9971e3edf)), closes [Magikcraft/nano-bpm#806](https://github.com/Magikcraft/nano-bpm/issues/806)
7
+
1
8
  # [0.73.0](https://github.com/nanobpm/nano-workforce/compare/v0.72.1...v0.73.0) (2026-08-15)
2
9
 
3
10
 
@@ -3,7 +3,7 @@
3
3
  // the merge-exclusion graph. Force the token transport and stub `globalThis.fetch`.
4
4
  import { test } from "node:test";
5
5
  import { assertEquals, assertRejects } from "#test-assert";
6
- import { BaseBranchMustExistError, ensureBaseBranch, fetchPrFiles } from "./github.ts";
6
+ import { BaseBranchMustExistError, ensureBaseBranch, fetchPrFiles, isNotAPullRequestError } from "./github.ts";
7
7
 
8
8
  // A fake `fetch` that serves `pages` of file batches; each page N (1-based) returns `pages[N-1]`
9
9
  // files (named `f{index}`), setting a `Link: rel="next"` header whenever a later page exists.
@@ -236,3 +236,26 @@ test("ensureBaseBranch: missing non-epic/* branch throws BaseBranchMustExistErro
236
236
  // A rejected non-epic/* base must never spawn a wrong-rooted branch.
237
237
  assertEquals(state.creates.length, 0);
238
238
  });
239
+
240
+ // A `Depends-on:` ref that resolves to an issue (or a non-existent number) can never merge, so the
241
+ // merge-poller's dependency gate must treat it as non-blocking rather than wedging forever — the
242
+ // exact wedge behind Magikcraft/nano-bpm#806 declaring `Depends-on:` its epic tracking *issue*
243
+ // #796. `isNotAPullRequestError` is the discriminator: it must fire for both transports' "not a PR"
244
+ // signals and stay false for transient failures (which must keep the dependency blocking).
245
+ test("isNotAPullRequestError: gh GraphQL 'not a PullRequest' → true", () => {
246
+ const err = new Error(
247
+ "GraphQL: Could not resolve to a PullRequest with the number of 796. (repository.pullRequest)",
248
+ );
249
+ assertEquals(isNotAPullRequestError(err), true);
250
+ });
251
+
252
+ test("isNotAPullRequestError: token-mode 404 → true", () => {
253
+ assertEquals(isNotAPullRequestError(new Error("github 404 Not Found")), true);
254
+ });
255
+
256
+ test("isNotAPullRequestError: transient failures stay blocking (false)", () => {
257
+ assertEquals(isNotAPullRequestError(new Error("github 502 Bad Gateway")), false);
258
+ assertEquals(isNotAPullRequestError(new Error("API rate limit exceeded")), false);
259
+ assertEquals(isNotAPullRequestError(new Error("fetch failed")), false);
260
+ assertEquals(isNotAPullRequestError(null), false);
261
+ });
package/app/github.ts CHANGED
@@ -496,6 +496,20 @@ function allCheckNames(rollup: RollupEntry[]): string[] {
496
496
  return names;
497
497
  }
498
498
 
499
+ /** True when `err` is GitHub reporting that a ref which parsed as `owner/repo#N` is not a pull
500
+ * request — either it's an issue (issues and PRs share GitHub's number space, so an issue number
501
+ * is indistinguishable from a PR number by shape alone) or the number does not exist. Both
502
+ * transports surface here: `gh` mode throws the GraphQL message "Could not resolve to a
503
+ * PullRequest with the number of N", and token mode throws `github 404 …` from
504
+ * `GET /repos/{repo}/pulls/{N}`. A ref that is not a pull request can never merge, so a caller
505
+ * gating a merge queue on it (see `isDepMerged`) must treat it as non-blocking instead of wedging
506
+ * forever. Transient failures (rate-limit, 5xx, network) deliberately return `false` so the caller
507
+ * keeps waiting/retrying rather than silently clearing a real dependency. */
508
+ export function isNotAPullRequestError(err: unknown): boolean {
509
+ const msg = err instanceof Error ? err.message : String(err);
510
+ return /could not resolve to a pullrequest/i.test(msg) || /\bgithub 404\b/i.test(msg);
511
+ }
512
+
499
513
  export async function fetchPrState(
500
514
  repo: string,
501
515
  number: number | string,
package/app/service.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  fetchPrReviews,
21
21
  fetchPrState,
22
22
  hasPendingCopilotReviewer,
23
+ isNotAPullRequestError,
23
24
  type MergeMethod,
24
25
  type PrState,
25
26
  requestCopilotReview,
@@ -738,8 +739,23 @@ async function isDepMerged(data: DataLayer, depKey: string, token: string): Prom
738
739
  if (tracked && tracked.status === "merged") return true;
739
740
  const parsed = parsePr(depKey);
740
741
  if (!parsed) return true; // unparseable dep can't be checked on GitHub → treat as cleared so it never wedges the PR
741
- const st = await fetchPrState(parsed.repo, parsed.number, token);
742
- return st?.merged ?? false;
742
+ try {
743
+ const st = await fetchPrState(parsed.repo, parsed.number, token);
744
+ return st?.merged ?? false;
745
+ } catch (err) {
746
+ // A ref that GitHub cannot resolve to a *pull request* — it's an issue (issues and PRs share
747
+ // GitHub's number space) or the number doesn't exist — can never merge, so it cannot gate a
748
+ // merge queue. Treat it as cleared (non-blocking) rather than wedging the run at `wait-deps`
749
+ // forever, as happened when a PR body declared `Depends-on:` its epic tracking *issue*
750
+ // (Magikcraft/nano-bpm#806 → #796). Transient failures rethrow so the poller logs and retries.
751
+ if (isNotAPullRequestError(err)) {
752
+ console.warn(
753
+ `[poller] dep ${depKey} is not a mergeable pull request (issue or missing) — treating as non-blocking`,
754
+ );
755
+ return true;
756
+ }
757
+ throw err;
758
+ }
743
759
  }
744
760
 
745
761
  /** Flip a PR into the transient `merging` status and publish the correlating message, reverting
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanobpm/nano-workforce",
3
- "version": "0.73.0",
3
+ "version": "0.73.1",
4
4
  "description": "Nano Workforce — an Agent Graph Orchestration application for Agentic SDLC: durable BPMN processes that coordinate a graph of AI agents across the software delivery lifecycle.",
5
5
  "type": "module",
6
6
  "main": "main.ts",