@nanobpm/nano-workforce 0.36.0 → 0.37.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.
Files changed (51) hide show
  1. package/.github/workflows/ci.yml +7 -17
  2. package/AGENTS.md +8 -7
  3. package/CHANGELOG.md +7 -0
  4. package/README.md +1 -21
  5. package/actions/abandon.test.ts +8 -16
  6. package/actions/blackboard.test.ts +13 -21
  7. package/app/abandon.test.ts +10 -15
  8. package/app/baseGuard.test.ts +7 -6
  9. package/app/blackboard.test.ts +22 -32
  10. package/app/ensure-pr.test.ts +10 -12
  11. package/app/github.test.ts +9 -8
  12. package/app/github.ts +1 -21
  13. package/app/instance-tracking.test.ts +8 -6
  14. package/app/mergeExclusion.test.ts +11 -20
  15. package/app/mergeProtocol.test.ts +14 -13
  16. package/app/mergeRebaseArm.test.ts +8 -6
  17. package/app/mergeTrain.test.ts +10 -9
  18. package/app/persist-escalation.test.ts +9 -30
  19. package/app/persist-round.test.ts +6 -19
  20. package/app/plan.test.ts +19 -42
  21. package/app/record-plan-review.test.ts +8 -7
  22. package/app/retro.test.ts +24 -48
  23. package/app/reviewWait.test.ts +12 -11
  24. package/app/rounds.test.ts +13 -12
  25. package/app/service.test.ts +14 -27
  26. package/app/taskDelta.test.ts +8 -17
  27. package/app/trialMerge.test.ts +4 -3
  28. package/app/version.ts +6 -21
  29. package/app/waves.test.ts +17 -16
  30. package/operations/getVersion.test.ts +8 -12
  31. package/operations/getVersion.ts +2 -3
  32. package/operations/listActivePrs.test.ts +8 -14
  33. package/operations/listActivePrs.ts +2 -3
  34. package/operations/startAndMessage.test.ts +6 -12
  35. package/package.json +6 -4
  36. package/scripts/check-agent-prompts.test.ts +15 -11
  37. package/scripts/layout-bpmn.ts +6 -28
  38. package/scripts/pages-contract.test.ts +14 -13
  39. package/scripts/purge-db.ts +1 -1
  40. package/scripts/upgrade-from-pack.ts +1 -1
  41. package/test/assert.ts +74 -0
  42. package/tsconfig.json +4 -1
  43. package/workers/record-plan-review/worker.test.ts +6 -9
  44. package/workers/record-results/worker.test.ts +5 -8
  45. package/workers/record-trial-merge/worker.test.ts +7 -18
  46. package/workers/record-wave/worker.test.ts +13 -20
  47. package/workers/retro-gather/worker.test.ts +4 -17
  48. package/workers/retro-record/worker.test.ts +8 -27
  49. package/workers/select-wave/worker.test.ts +5 -8
  50. package/deno.json +0 -24
  51. package/deno.lock +0 -1777
@@ -6,7 +6,8 @@
6
6
  // non-retryable `NO_WORK_DISPATCHED` BpmnError (→ incident) when the epic finalizes with no opened
7
7
  // PR, recording a `failed` terminal status + outcome first, so "accomplished nothing" surfaces
8
8
  // instead of masquerading as a completed epic.
9
- import { assertEquals, assertRejects } from "jsr:@std/assert@1";
9
+ import { test } from "node:test";
10
+ import { assertEquals, assertRejects } from "#test-assert";
10
11
  import { BpmnError } from "@nanobpm/urban";
11
12
  import handler from "./worker.ts";
12
13
  import type { PlanTaskStatus } from "../../app/plan.ts";
@@ -25,7 +26,6 @@ function fakeApp(rows: Row[]) {
25
26
  table(name: string, key: string) {
26
27
  if (name === "plans") {
27
28
  return {
28
- // deno-lint-ignore no-explicit-any
29
29
  update: (k: any, patch: any) => {
30
30
  plans.push({ [key]: k, ...patch });
31
31
  return Promise.resolve(patch);
@@ -34,7 +34,6 @@ function fakeApp(rows: Row[]) {
34
34
  }
35
35
  // plan_tasks
36
36
  return {
37
- // deno-lint-ignore no-explicit-any
38
37
  find: (q: any) =>
39
38
  Promise.resolve(
40
39
  rows.filter((r) =>
@@ -48,15 +47,13 @@ function fakeApp(rows: Row[]) {
48
47
  },
49
48
  log: () => {},
50
49
  _plans: plans,
51
- // deno-lint-ignore no-explicit-any
52
50
  } as any;
53
51
  }
54
52
 
55
53
  const call = async (app: unknown, planKey = "o/r#1") =>
56
- // deno-lint-ignore no-explicit-any
57
54
  await handler({ variables: { planKey } } as any, app as any);
58
55
 
59
- Deno.test("no opened PRs (empty plan) hard-fails with NO_WORK_DISPATCHED", async () => {
56
+ test("no opened PRs (empty plan) hard-fails with NO_WORK_DISPATCHED", async () => {
60
57
  const app = fakeApp([]);
61
58
  const err = await assertRejects(() => call(app), BpmnError);
62
59
  assertEquals((err as BpmnError).errorCode, "NO_WORK_DISPATCHED");
@@ -67,7 +64,7 @@ Deno.test("no opened PRs (empty plan) hard-fails with NO_WORK_DISPATCHED", async
67
64
  assertEquals(plan.outcome, "no work dispatched — the planner produced no tasks");
68
65
  });
69
66
 
70
- Deno.test("tasks present but none opened (all skipped/blocked) hard-fails", async () => {
67
+ test("tasks present but none opened (all skipped/blocked) hard-fails", async () => {
71
68
  const app = fakeApp([
72
69
  { id: 1, plan_key: "o/r#1", task_id: "a", status: "skipped" },
73
70
  { id: 2, plan_key: "o/r#1", task_id: "b", status: "blocked" },
@@ -79,7 +76,7 @@ Deno.test("tasks present but none opened (all skipped/blocked) hard-fails", asyn
79
76
  assertEquals(plan.outcome, "no work dispatched — every task was blocked or skipped");
80
77
  });
81
78
 
82
- Deno.test("at least one opened PR finalizes cleanly (no throw)", async () => {
79
+ test("at least one opened PR finalizes cleanly (no throw)", async () => {
83
80
  const app = fakeApp([
84
81
  { id: 1, plan_key: "o/r#1", task_id: "a", status: "opened" },
85
82
  { id: 2, plan_key: "o/r#1", task_id: "b", status: "skipped" },
@@ -1,4 +1,5 @@
1
- import { assertEquals, assertStringIncludes } from "jsr:@std/assert@1";
1
+ import { test } from "node:test";
2
+ import { assertEquals, assertStringIncludes } from "#test-assert";
2
3
  import handler, { parseResult } from "./worker.ts";
3
4
 
4
5
  function fakeApp() {
@@ -7,16 +8,13 @@ function fakeApp() {
7
8
  data: {
8
9
  table(name: string, _key: string) {
9
10
  return {
10
- // deno-lint-ignore require-await
11
11
  async find(_q: unknown) {
12
12
  return [];
13
13
  },
14
- // deno-lint-ignore require-await
15
14
  async insert(row: unknown) {
16
15
  (inserts[name] ??= []).push(row);
17
16
  return 7;
18
17
  },
19
- // deno-lint-ignore require-await
20
18
  async update(_key: unknown, _patch: unknown) {},
21
19
  };
22
20
  },
@@ -26,31 +24,27 @@ function fakeApp() {
26
24
  return { app, inserts };
27
25
  }
28
26
 
29
- Deno.test("record-trial-merge shapes clean results as proceed", async () => {
27
+ test("record-trial-merge shapes clean results as proceed", async () => {
30
28
  const { app, inserts } = fakeApp();
31
29
  const out = await handler(
32
- // deno-lint-ignore no-explicit-any
33
30
  { key: 11, variables: { planKey: "o/r#69", currentWave: 2, result: "clean", summary: "green" } } as any,
34
- // deno-lint-ignore no-explicit-any
35
31
  app as any,
36
32
  ) as Record<string, unknown>;
37
33
 
38
34
  assertEquals(out.trialMergeRed, false);
39
35
  assertEquals(inserts.plan_trial_merges.length, 1);
40
- // deno-lint-ignore no-explicit-any
41
36
  assertEquals((inserts.plan_trial_merges[0] as any).result, "clean");
42
37
  });
43
38
 
44
- Deno.test("parseResult trims valid agent result strings", () => {
39
+ test("parseResult trims valid agent result strings", () => {
45
40
  assertEquals(parseResult("clean\n"), "clean");
46
41
  assertEquals(parseResult(" merge-conflict "), "merge-conflict");
47
42
  assertEquals(parseResult("unknown"), "suite-failed");
48
43
  });
49
44
 
50
- Deno.test("record-trial-merge escalates only suite-failed results", async () => {
45
+ test("record-trial-merge escalates only suite-failed results", async () => {
51
46
  const { app } = fakeApp();
52
47
  const out = await handler(
53
- // deno-lint-ignore no-explicit-any
54
48
  {
55
49
  key: 12,
56
50
  variables: {
@@ -61,7 +55,6 @@ Deno.test("record-trial-merge escalates only suite-failed results", async () =>
61
55
  failing: ["integration suite"],
62
56
  },
63
57
  } as any,
64
- // deno-lint-ignore no-explicit-any
65
58
  app as any,
66
59
  ) as Record<string, unknown>;
67
60
 
@@ -71,10 +64,9 @@ Deno.test("record-trial-merge escalates only suite-failed results", async () =>
71
64
  assertStringIncludes(String(out.question ?? ""), "proceed");
72
65
  });
73
66
 
74
- Deno.test("record-trial-merge renders odd failing payloads without throwing", async () => {
67
+ test("record-trial-merge renders odd failing payloads without throwing", async () => {
75
68
  const { app } = fakeApp();
76
69
  const out = await handler(
77
- // deno-lint-ignore no-explicit-any
78
70
  {
79
71
  variables: {
80
72
  planKey: "o/r#69",
@@ -83,7 +75,6 @@ Deno.test("record-trial-merge renders odd failing payloads without throwing", as
83
75
  failing: [1n],
84
76
  },
85
77
  } as any,
86
- // deno-lint-ignore no-explicit-any
87
78
  app as any,
88
79
  ) as Record<string, unknown>;
89
80
 
@@ -91,12 +82,10 @@ Deno.test("record-trial-merge renders odd failing payloads without throwing", as
91
82
  assertStringIncludes(String(out.question ?? ""), "Failing:");
92
83
  });
93
84
 
94
- Deno.test("record-trial-merge treats textual conflicts as pass-through", async () => {
85
+ test("record-trial-merge treats textual conflicts as pass-through", async () => {
95
86
  const { app } = fakeApp();
96
87
  const out = await handler(
97
- // deno-lint-ignore no-explicit-any
98
88
  { variables: { planKey: "o/r#69", result: "merge-conflict", conflicts: [{ prs: [1, 2] }] } } as any,
99
- // deno-lint-ignore no-explicit-any
100
89
  app as any,
101
90
  ) as Record<string, unknown>;
102
91
 
@@ -1,7 +1,8 @@
1
1
  // Red/green regression for retrying a wave when `select-wave` deliberately left pending work
2
2
  // behind a non-fatal wait (D7 / issue #63). Advancing past that wave would make `record-results`
3
3
  // finish the plan with a still-pending task.
4
- import { assertEquals } from "jsr:@std/assert@1";
4
+ import { test } from "node:test";
5
+ import { assertEquals } from "#test-assert";
5
6
  import handler from "./worker.ts";
6
7
  import type { PlanTaskStatus } from "../../app/plan.ts";
7
8
  import { _clearMergeProtocolCache } from "../../app/mergeProtocol.ts";
@@ -32,10 +33,8 @@ function fakeApp(rows: Row[]) {
32
33
  table(name: string, key: string) {
33
34
  const store = stores[name] ??= [];
34
35
  return {
35
- // deno-lint-ignore no-explicit-any
36
36
  get: (k: any) =>
37
37
  Promise.resolve(store.find((r) => ((r as unknown) as Record<string, unknown>)[key] === k)),
38
- // deno-lint-ignore no-explicit-any
39
38
  find: (q: any) =>
40
39
  Promise.resolve(
41
40
  store.filter((r) =>
@@ -44,12 +43,10 @@ function fakeApp(rows: Row[]) {
44
43
  )
45
44
  ),
46
45
  ),
47
- // deno-lint-ignore no-explicit-any
48
46
  insert: (row: any) => {
49
47
  store.push(row);
50
48
  return Promise.resolve(store.length);
51
49
  },
52
- // deno-lint-ignore no-explicit-any
53
50
  update: (k: any, patch: any) => {
54
51
  if (name === "plans") {
55
52
  planUpdates.push({ key: k, patch });
@@ -68,7 +65,6 @@ function fakeApp(rows: Row[]) {
68
65
  engine: {
69
66
  createInstance: () => Promise.resolve({ processInstanceKey: "pi" }),
70
67
  },
71
- // deno-lint-ignore no-explicit-any
72
68
  } as any,
73
69
  planUpdates,
74
70
  };
@@ -76,11 +72,11 @@ function fakeApp(rows: Row[]) {
76
72
 
77
73
  function installGithubStub(method: "gh-merge" | "mergify-queue") {
78
74
  _clearMergeProtocolCache();
79
- const oldTransport = Deno.env.get("NANO_PR_GITHUB_TRANSPORT");
80
- const oldToken = Deno.env.get("GITHUB_TOKEN");
75
+ const oldTransport = process.env["NANO_PR_GITHUB_TRANSPORT"];
76
+ const oldToken = process.env["GITHUB_TOKEN"];
81
77
  const oldFetch = globalThis.fetch;
82
- Deno.env.set("NANO_PR_GITHUB_TRANSPORT", "token");
83
- Deno.env.set("GITHUB_TOKEN", "test-token");
78
+ process.env["NANO_PR_GITHUB_TRANSPORT"] = "token";
79
+ process.env["GITHUB_TOKEN"] = "test-token";
84
80
  globalThis.fetch = ((input: string | URL | Request) => {
85
81
  const url = String(input);
86
82
  if (url.includes("/contents/AGENTS.md")) {
@@ -104,16 +100,16 @@ function installGithubStub(method: "gh-merge" | "mergify-queue") {
104
100
  return Promise.resolve(new Response("not found", { status: 404 }));
105
101
  }) as typeof fetch;
106
102
  return () => {
107
- if (oldTransport == null) Deno.env.delete("NANO_PR_GITHUB_TRANSPORT");
108
- else Deno.env.set("NANO_PR_GITHUB_TRANSPORT", oldTransport);
109
- if (oldToken == null) Deno.env.delete("GITHUB_TOKEN");
110
- else Deno.env.set("GITHUB_TOKEN", oldToken);
103
+ if (oldTransport == null) delete process.env["NANO_PR_GITHUB_TRANSPORT"];
104
+ else process.env["NANO_PR_GITHUB_TRANSPORT"] = oldTransport;
105
+ if (oldToken == null) delete process.env["GITHUB_TOKEN"];
106
+ else process.env["GITHUB_TOKEN"] = oldToken;
111
107
  globalThis.fetch = oldFetch;
112
108
  _clearMergeProtocolCache();
113
109
  };
114
110
  }
115
111
 
116
- Deno.test("record-wave retries the same wave when a task is still pending", async () => {
112
+ test("record-wave retries the same wave when a task is still pending", async () => {
117
113
  const rows: Row[] = [{
118
114
  id: 2,
119
115
  plan_key: "owner/repo#63",
@@ -124,7 +120,6 @@ Deno.test("record-wave retries the same wave when a task is still pending", asyn
124
120
  const { app, planUpdates } = fakeApp(rows);
125
121
 
126
122
  const out = await handler(
127
- // deno-lint-ignore no-explicit-any
128
123
  {
129
124
  variables: {
130
125
  planKey: "owner/repo#63",
@@ -148,7 +143,7 @@ Deno.test("record-wave retries the same wave when a task is still pending", asyn
148
143
  assertEquals((planUpdates[0].patch as Record<string, unknown>).gate_wave, 1);
149
144
  });
150
145
 
151
- Deno.test("record-wave skips trial merge for mergify-queue repos with 2+ heads", async () => {
146
+ test("record-wave skips trial merge for mergify-queue repos with 2+ heads", async () => {
152
147
  const restore = installGithubStub("mergify-queue");
153
148
  try {
154
149
  const rows: Row[] = [
@@ -157,7 +152,6 @@ Deno.test("record-wave skips trial merge for mergify-queue repos with 2+ heads",
157
152
  ];
158
153
  const { app } = fakeApp(rows);
159
154
  const out = await handler(
160
- // deno-lint-ignore no-explicit-any
161
155
  {
162
156
  variables: {
163
157
  planKey: "owner/repo#69",
@@ -184,7 +178,7 @@ Deno.test("record-wave skips trial merge for mergify-queue repos with 2+ heads",
184
178
  }
185
179
  });
186
180
 
187
- Deno.test("record-wave runs trial merge for non-queue repos with populated heads", async () => {
181
+ test("record-wave runs trial merge for non-queue repos with populated heads", async () => {
188
182
  const restore = installGithubStub("gh-merge");
189
183
  try {
190
184
  const rows: Row[] = [
@@ -193,7 +187,6 @@ Deno.test("record-wave runs trial merge for non-queue repos with populated heads
193
187
  ];
194
188
  const { app } = fakeApp(rows);
195
189
  const out = await handler(
196
- // deno-lint-ignore no-explicit-any
197
190
  {
198
191
  variables: {
199
192
  planKey: "owner/repo#69",
@@ -1,47 +1,38 @@
1
- import { assert, assertEquals, assertStringIncludes } from "jsr:@std/assert@1";
1
+ import { test } from "node:test";
2
+ import { assert, assertEquals, assertStringIncludes } from "#test-assert";
2
3
  import type { DataLayer } from "@nanobpm/urban";
3
4
  import { appendEntry } from "../../app/blackboard.ts";
4
5
  import handler from "./worker.ts";
5
6
 
6
- // deno-lint-ignore no-explicit-any
7
7
  function memData(): { data: DataLayer; stores: Record<string, any[]> } {
8
- // deno-lint-ignore no-explicit-any
9
8
  const stores: Record<string, any[]> = {};
10
9
  const seq: Record<string, number> = {};
11
10
  function tbl(name: string, pk = "id") {
12
- // deno-lint-ignore no-explicit-any
13
11
  const rows = (stores[name] ??= [] as any[]);
14
- // deno-lint-ignore no-explicit-any
15
12
  const match = (r: any, where: any) => Object.entries(where).every(([k, v]) => r[k] === v);
16
13
  return {
17
- // deno-lint-ignore no-explicit-any require-await
18
14
  async insert(row: any) {
19
15
  const id = (seq[name] = (seq[name] ?? 0) + 1);
20
16
  rows.push(pk === "id" ? { id, ...row } : { ...row });
21
17
  return pk === "id" ? id : row[pk];
22
18
  },
23
- // deno-lint-ignore no-explicit-any require-await
24
19
  async find(where: any = {}) {
25
20
  return rows.filter((r) => match(r, where));
26
21
  },
27
- // deno-lint-ignore no-explicit-any require-await
28
22
  async findOne(where: any = {}) {
29
23
  return rows.find((r) => match(r, where));
30
24
  },
31
- // deno-lint-ignore no-explicit-any require-await
32
25
  async get(id: any) {
33
26
  return rows.find((row) => row[pk] === id);
34
27
  },
35
- // deno-lint-ignore no-explicit-any require-await
36
28
  async update() {},
37
29
  };
38
30
  }
39
- // deno-lint-ignore no-explicit-any
40
31
  const data = { table: (n: string, pk?: string) => tbl(n, pk) } as any as DataLayer;
41
32
  return { data, stores };
42
33
  }
43
34
 
44
- Deno.test("retro-gather: emits a digest brief + learning count for the plan", async () => {
35
+ test("retro-gather: emits a digest brief + learning count for the plan", async () => {
45
36
  const { data, stores } = memData();
46
37
  stores["plans"] = [{ plan_key: "o/r#3", repo: "o/r", issue_url: "https://x/3", title: "Epic" }];
47
38
  await appendEntry(data, "o/r#3", { author_task: "t1", kind: "learning", body: "regen before build" });
@@ -49,9 +40,7 @@ Deno.test("retro-gather: emits a digest brief + learning count for the plan", as
49
40
 
50
41
  const app = { data, log: () => undefined };
51
42
  const out = await handler(
52
- // deno-lint-ignore no-explicit-any
53
43
  { variables: { planKey: "o/r#3" } } as any,
54
- // deno-lint-ignore no-explicit-any
55
44
  app as any,
56
45
  ) as Record<string, unknown>;
57
46
 
@@ -61,14 +50,12 @@ Deno.test("retro-gather: emits a digest brief + learning count for the plan", as
61
50
  assertStringIncludes(String(out.retroDigest), "o/r#3");
62
51
  });
63
52
 
64
- Deno.test("retro-gather: an epic with no learnings still renders a valid brief", async () => {
53
+ test("retro-gather: an epic with no learnings still renders a valid brief", async () => {
65
54
  const { data, stores } = memData();
66
55
  stores["plans"] = [{ plan_key: "o/r#4", repo: "o/r", issue_url: "", title: null }];
67
56
  const app = { data, log: () => undefined };
68
57
  const out = await handler(
69
- // deno-lint-ignore no-explicit-any
70
58
  { variables: { planKey: "o/r#4" } } as any,
71
- // deno-lint-ignore no-explicit-any
72
59
  app as any,
73
60
  ) as Record<string, unknown>;
74
61
 
@@ -1,35 +1,28 @@
1
- import { assertEquals } from "jsr:@std/assert@1";
1
+ import { test } from "node:test";
2
+ import { assertEquals } from "#test-assert";
2
3
  import handler from "./worker.ts";
3
4
 
4
5
  function fakeApp() {
5
- // deno-lint-ignore no-explicit-any
6
6
  const stores: Record<string, any[]> = { plan_retros: [] };
7
7
  const seq: Record<string, number> = {};
8
8
  function tbl(name: string, pk = "id") {
9
- // deno-lint-ignore no-explicit-any
10
9
  const rows = (stores[name] ??= [] as any[]);
11
- // deno-lint-ignore no-explicit-any
12
10
  const match = (r: any, where: any) => Object.entries(where).every(([k, v]) => r[k] === v);
13
11
  return {
14
- // deno-lint-ignore no-explicit-any require-await
15
12
  async insert(row: any) {
16
13
  const id = (seq[name] = (seq[name] ?? 0) + 1);
17
14
  rows.push(pk === "id" ? { id, ...row } : { ...row });
18
15
  return pk === "id" ? id : row[pk];
19
16
  },
20
- // deno-lint-ignore no-explicit-any require-await
21
17
  async find(where: any = {}) {
22
18
  return rows.filter((r) => match(r, where));
23
19
  },
24
- // deno-lint-ignore no-explicit-any require-await
25
20
  async findOne(where: any = {}) {
26
21
  return rows.find((r) => match(r, where));
27
22
  },
28
- // deno-lint-ignore no-explicit-any require-await
29
23
  async get(id: any) {
30
24
  return rows.find((row) => row[pk] === id);
31
25
  },
32
- // deno-lint-ignore no-explicit-any require-await
33
26
  async update(id: any, patch: any) {
34
27
  const r = rows.find((row) => row[pk] === id);
35
28
  if (r) Object.assign(r, patch);
@@ -40,10 +33,9 @@ function fakeApp() {
40
33
  return { app, stores };
41
34
  }
42
35
 
43
- Deno.test("retro-record: persists a filed retro from hoisted result vars", async () => {
36
+ test("retro-record: persists a filed retro from hoisted result vars", async () => {
44
37
  const { app, stores } = fakeApp();
45
38
  await handler(
46
- // deno-lint-ignore no-explicit-any
47
39
  {
48
40
  variables: {
49
41
  planKey: "o/r#5",
@@ -54,7 +46,6 @@ Deno.test("retro-record: persists a filed retro from hoisted result vars", async
54
46
  "io.nanobpm.agentResult": { output: "the full report" },
55
47
  },
56
48
  } as any,
57
- // deno-lint-ignore no-explicit-any
58
49
  app as any,
59
50
  );
60
51
 
@@ -67,59 +58,49 @@ Deno.test("retro-record: persists a filed retro from hoisted result vars", async
67
58
  assertEquals(row.report, "the full report");
68
59
  });
69
60
 
70
- Deno.test("retro-record: defaults to skipped when the agent filed no PR", async () => {
61
+ test("retro-record: defaults to skipped when the agent filed no PR", async () => {
71
62
  const { app, stores } = fakeApp();
72
63
  await handler(
73
- // deno-lint-ignore no-explicit-any
74
64
  { variables: { planKey: "o/r#6", summary: "nothing durable" } } as any,
75
- // deno-lint-ignore no-explicit-any
76
65
  app as any,
77
66
  );
78
67
  assertEquals(stores.plan_retros[0].status, "skipped");
79
68
  assertEquals(stores.plan_retros[0].pr_key, null);
80
69
  });
81
70
 
82
- Deno.test("retro-record: honours an explicit blocked status", async () => {
71
+ test("retro-record: honours an explicit blocked status", async () => {
83
72
  const { app, stores } = fakeApp();
84
73
  await handler(
85
- // deno-lint-ignore no-explicit-any
86
74
  { variables: { planKey: "o/r#7", status: "blocked", summary: "no write access" } } as any,
87
- // deno-lint-ignore no-explicit-any
88
75
  app as any,
89
76
  );
90
77
  assertEquals(stores.plan_retros[0].status, "blocked");
91
78
  });
92
79
 
93
- Deno.test("retro-record: ignores a PR unless the status is filed", async () => {
80
+ test("retro-record: ignores a PR unless the status is filed", async () => {
94
81
  const { app, stores } = fakeApp();
95
82
  await handler(
96
- // deno-lint-ignore no-explicit-any
97
83
  { variables: { planKey: "o/r#8", status: "skipped", pr: "o/r#43", summary: "not durable" } } as any,
98
- // deno-lint-ignore no-explicit-any
99
84
  app as any,
100
85
  );
101
86
  assertEquals(stores.plan_retros[0].status, "skipped");
102
87
  assertEquals(stores.plan_retros[0].pr_key, null);
103
88
  });
104
89
 
105
- Deno.test("retro-record: defaults invalid status from PR presence", async () => {
90
+ test("retro-record: defaults invalid status from PR presence", async () => {
106
91
  const { app, stores } = fakeApp();
107
92
  await handler(
108
- // deno-lint-ignore no-explicit-any
109
93
  { variables: { planKey: "o/r#9", status: "done", pr: "o/r#44" } } as any,
110
- // deno-lint-ignore no-explicit-any
111
94
  app as any,
112
95
  );
113
96
  assertEquals(stores.plan_retros[0].status, "filed");
114
97
  assertEquals(stores.plan_retros[0].pr_key, "o/r#44");
115
98
  });
116
99
 
117
- Deno.test("retro-record: coerces filed without a PR to skipped", async () => {
100
+ test("retro-record: coerces filed without a PR to skipped", async () => {
118
101
  const { app, stores } = fakeApp();
119
102
  await handler(
120
- // deno-lint-ignore no-explicit-any
121
103
  { variables: { planKey: "o/r#10", status: "filed", summary: "forgot the PR" } } as any,
122
- // deno-lint-ignore no-explicit-any
123
104
  app as any,
124
105
  );
125
106
  assertEquals(stores.plan_retros[0].status, "skipped");
@@ -3,7 +3,8 @@
3
3
  // A predecessor parked behind a merge lane is not a failed slice: dependents must remain pending
4
4
  // so a later retry can dispatch them once the lane clears. Real non-open failures still cascade
5
5
  // to `skipped` as before.
6
- import { assertEquals } from "jsr:@std/assert@1";
6
+ import { test } from "node:test";
7
+ import { assertEquals } from "#test-assert";
7
8
  import handler from "./worker.ts";
8
9
  import type { PlanTaskStatus } from "../../app/plan.ts";
9
10
 
@@ -30,7 +31,6 @@ function fakeApp(rows: Row[], deps: DepRow[]) {
30
31
  table(name: string, key: string) {
31
32
  const store = name === "plan_tasks" ? rows : deps;
32
33
  return {
33
- // deno-lint-ignore no-explicit-any
34
34
  find: (q: any) =>
35
35
  Promise.resolve(
36
36
  store.filter((r) =>
@@ -39,7 +39,6 @@ function fakeApp(rows: Row[], deps: DepRow[]) {
39
39
  )
40
40
  ),
41
41
  ),
42
- // deno-lint-ignore no-explicit-any
43
42
  update: (k: any, patch: any) => {
44
43
  const row = store.find((r) =>
45
44
  ((r as unknown) as Record<string, unknown>)[key] === k
@@ -50,20 +49,18 @@ function fakeApp(rows: Row[], deps: DepRow[]) {
50
49
  };
51
50
  },
52
51
  },
53
- // deno-lint-ignore no-explicit-any
54
52
  } as any;
55
53
  }
56
54
 
57
55
  async function selectWave(rows: Row[], deps: DepRow[]) {
58
56
  const out = await handler(
59
- // deno-lint-ignore no-explicit-any
60
57
  { variables: { planKey: "owner/repo#63", currentWave: 1 } } as any,
61
58
  fakeApp(rows, deps),
62
59
  );
63
60
  return out as { waveTasks: unknown[] };
64
61
  }
65
62
 
66
- Deno.test("select-wave leaves dependents pending behind a waiting-for-lane dependency", async () => {
63
+ test("select-wave leaves dependents pending behind a waiting-for-lane dependency", async () => {
67
64
  const rows: Row[] = [
68
65
  {
69
66
  id: 1,
@@ -95,9 +92,9 @@ Deno.test("select-wave leaves dependents pending behind a waiting-for-lane depen
95
92
  assertEquals(rows[1].summary, undefined);
96
93
  });
97
94
 
98
- Deno.test("select-wave still skips dependents behind failed or otherwise non-open dependencies", async (t) => {
95
+ test("select-wave still skips dependents behind failed or otherwise non-open dependencies", async (t) => {
99
96
  for (const depStatus of ["blocked", "skipped", "pending"] as const) {
100
- await t.step(depStatus, async () => {
97
+ await t.test(depStatus, async () => {
101
98
  const rows: Row[] = [
102
99
  {
103
100
  id: 1,
package/deno.json DELETED
@@ -1,24 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "lib": [
4
- "deno.window",
5
- "deno.ns"
6
- ]
7
- },
8
- "imports": {
9
- "@nanobpm/urban": "npm:@nanobpm/urban@^0.33.0"
10
- },
11
- "tasks": {
12
- "start": "deno run --allow-net --allow-read --allow-write --allow-run=gh --allow-env main.ts",
13
- "purge": "deno run --allow-read --allow-write --allow-env scripts/purge-db.ts",
14
- "check": "deno run -A @nanobpm/urban check",
15
- "check:prompts": "deno run -A scripts/check-agent-prompts.ts",
16
- "gen": "deno run -A @nanobpm/urban gen",
17
- "gen:check": "deno run -A @nanobpm/urban gen --check",
18
- "layout": "deno run -A scripts/layout-bpmn.ts",
19
- "layout:check": "deno run -A scripts/layout-bpmn.ts --check",
20
- "dev": "deno run -A @nanobpm/urban dev",
21
- "compile": "deno compile --allow-net --allow-read --allow-write --allow-run=gh --allow-env -o dist/nano-workforce main.ts",
22
- "test": "deno test -A"
23
- }
24
- }