@nanobpm/nano-workforce 0.203.0 → 0.204.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/CHANGELOG.md +12 -0
- package/app/feature.ts +9 -0
- package/app/github.ts +14 -6
- package/app/issuePrLink.test.ts +703 -0
- package/app/issuePrLink.ts +312 -0
- package/app/service.ts +3 -0
- package/db/migrations/125_feature_runs_pr_linked.sql +78 -0
- package/docs/agent-guide.md +282 -855
- package/e2e/feature-run.e2e.ts +58 -1
- package/e2e/plan-fanout.e2e.ts +24 -1
- package/e2e/support/github-admit.ts +33 -0
- package/package.json +1 -1
- package/resources/processes/feature.bpmn +1 -0
- package/resources/processes/implement-cell.bpmn +1 -0
- package/skills/nano-workforce/SKILL.md +20 -65
- package/workers/record-feature/worker.test.ts +256 -0
- package/workers/record-feature/worker.ts +27 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [0.204.0](https://github.com/nanobpm/nano-workforce/compare/v0.203.1...v0.204.0) (2026-11-04)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **feature-run:** post one idempotent "PR opened" comment on the claimed issue ([#912](https://github.com/nanobpm/nano-workforce/issues/912)) ([6c9219a](https://github.com/nanobpm/nano-workforce/commit/6c9219a8e2ca8ba3c495e07c20ee4ae9c3bf60be)), closes [camunda/camunda#19090](https://github.com/camunda/camunda/issues/19090) [#n](https://github.com/nanobpm/nano-workforce/issues/n) [#907](https://github.com/nanobpm/nano-workforce/issues/907) [#907](https://github.com/nanobpm/nano-workforce/issues/907)
|
|
6
|
+
|
|
7
|
+
## [0.203.1](https://github.com/nanobpm/nano-workforce/compare/v0.203.0...v0.203.1) (2026-11-03)
|
|
8
|
+
|
|
9
|
+
### Documentation
|
|
10
|
+
|
|
11
|
+
* shorten the operator guide and skill (64 KB → 26 KB) ([#909](https://github.com/nanobpm/nano-workforce/issues/909)) ([3a0be17](https://github.com/nanobpm/nano-workforce/commit/3a0be17b9f061c006e718f38773ebfffdc690cb4)), closes [sibling/#NNN](https://github.com/sibling/nano-workforce/issues/NNN) [#908](https://github.com/nanobpm/nano-workforce/issues/908) [#666](https://github.com/nanobpm/nano-workforce/issues/666)
|
|
12
|
+
|
|
1
13
|
## [0.203.0](https://github.com/nanobpm/nano-workforce/compare/v0.202.0...v0.203.0) (2026-10-13)
|
|
2
14
|
|
|
3
15
|
### Features
|
package/app/feature.ts
CHANGED
|
@@ -74,6 +74,15 @@ export interface FeatureRun {
|
|
|
74
74
|
* outcome is written to `status` itself; this carries the sub-state / note (e.g. "merged",
|
|
75
75
|
* "waiting_review", or "operator: <note>"). */
|
|
76
76
|
delivery_label: string | null;
|
|
77
|
+
/** CONFIRMED "PR opened" link (issue #907, app/issuePrLink.ts): the `owner/repo#N` PR key whose
|
|
78
|
+
* comment is known to be on the claimed issue (set only after a successful POST or marker match). */
|
|
79
|
+
pr_linked_pr_key?: string | null;
|
|
80
|
+
/** The PR key the claimed issue is OWED a "PR opened" link for (set by pr.record-feature for a
|
|
81
|
+
* claimed single-issue run only). Outstanding while it differs from `pr_linked_pr_key`; the
|
|
82
|
+
* poller's `pollIssuePrLinks` pass retries it until confirmed. */
|
|
83
|
+
pr_link_want_pr_key?: string | null;
|
|
84
|
+
/** In-flight link lease (ISO time) — a CAS-claimed, EXPIRING claim before any GitHub write. */
|
|
85
|
+
pr_link_lease_at?: string | null;
|
|
77
86
|
/** Timestamp an operator dismissed a TERMINAL run (§5, `acknowledge-done`); NULL until then. When
|
|
78
87
|
* set on a terminal row, `list_bucket` flips from 'active' to 'history'. Projection surface. */
|
|
79
88
|
acknowledged_at: string | null;
|
package/app/github.ts
CHANGED
|
@@ -40,14 +40,16 @@ export function githubTransport(): GithubTransport {
|
|
|
40
40
|
|
|
41
41
|
/** Run the host `gh` CLI with the given args (no shell — args are passed as a vector, so a
|
|
42
42
|
* `repo`/`number` from the datastore cannot inject a command). Resolves stdout, rejects on a
|
|
43
|
-
* non-zero exit with stderr as the message.
|
|
44
|
-
|
|
43
|
+
* non-zero exit with stderr as the message. Exported so sibling host-glue modules (e.g.
|
|
44
|
+
* app/issuePrLink.ts) ride the ONE gh transport instead of re-deriving it. */
|
|
45
|
+
export async function runGh(args: string[], opts: { timeoutMs?: number } = {}): Promise<string> {
|
|
45
46
|
const { execFile } = await import("node:child_process");
|
|
46
47
|
return await new Promise<string>((resolve, reject) => {
|
|
47
48
|
execFile(
|
|
48
49
|
"gh",
|
|
49
50
|
args,
|
|
50
|
-
|
|
51
|
+
// `timeout` (0 = none) kills a hung `gh` so a bounded caller's deadline is actually enforced.
|
|
52
|
+
{ maxBuffer: 16 * 1024 * 1024, timeout: opts.timeoutMs ?? 0 },
|
|
51
53
|
(err, stdout, stderr) => {
|
|
52
54
|
if (err) reject(new Error(String(stderr || "").trim() || err.message));
|
|
53
55
|
else resolve(String(stdout));
|
|
@@ -714,8 +716,9 @@ export async function requestCopilotReview(
|
|
|
714
716
|
// The same two-transport model (gh | token) backs the merge stage: read a PR's merge state to
|
|
715
717
|
// decide when it is landable, and perform the merge (directly or via the repo's merge queue).
|
|
716
718
|
|
|
717
|
-
/** Whether to use the `gh` CLI for this pass, honouring `NANO_PR_GITHUB_TRANSPORT`.
|
|
718
|
-
|
|
719
|
+
/** Whether to use the `gh` CLI for this pass, honouring `NANO_PR_GITHUB_TRANSPORT`. Exported so
|
|
720
|
+
* sibling host-glue modules (e.g. app/issuePrLink.ts) resolve the SAME transport selection. */
|
|
721
|
+
export async function useGh(): Promise<boolean> {
|
|
719
722
|
const mode = githubTransport();
|
|
720
723
|
return mode === "gh" || (mode === "auto" && (await isGhAvailable()));
|
|
721
724
|
}
|
|
@@ -739,9 +742,13 @@ export async function fetchPrMeta(
|
|
|
739
742
|
repo: string,
|
|
740
743
|
number: number | string,
|
|
741
744
|
token: string,
|
|
745
|
+
timeoutMs?: number,
|
|
742
746
|
): Promise<PrMeta | null> {
|
|
743
747
|
if (await useGh()) {
|
|
744
|
-
const out = await runGh(
|
|
748
|
+
const out = await runGh(
|
|
749
|
+
["pr", "view", String(number), "--repo", repo, "--json", "title,body,headRefName,baseRefName"],
|
|
750
|
+
timeoutMs === undefined ? undefined : { timeoutMs },
|
|
751
|
+
);
|
|
745
752
|
// biome-ignore lint/plugin: runtime/framework contract boundary for external data shape
|
|
746
753
|
const j = JSON.parse(out) as { title?: string; body?: string; headRefName?: string | null; baseRefName?: string | null };
|
|
747
754
|
return { title: j.title ?? null, body: j.body ?? "", headRef: j.headRefName ?? null, baseRef: j.baseRefName ?? null };
|
|
@@ -749,6 +756,7 @@ export async function fetchPrMeta(
|
|
|
749
756
|
if (!token) return null;
|
|
750
757
|
const r = await fetch(`https://api.github.com/repos/${repo}/pulls/${number}`, {
|
|
751
758
|
headers: { authorization: `Bearer ${token}`, accept: "application/vnd.github+json" },
|
|
759
|
+
...(timeoutMs === undefined ? {} : { signal: AbortSignal.timeout(timeoutMs) }),
|
|
752
760
|
});
|
|
753
761
|
if (!r.ok) throw new Error(`github ${r.status} ${r.statusText}`.trim());
|
|
754
762
|
// biome-ignore lint/plugin: runtime/framework contract boundary for external data shape
|