@nanobpm/nano-workforce 0.171.9 → 0.171.10
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 +6 -0
- package/app/contracts.ts +2 -2
- package/app/repoEnvelope.ts +14 -9
- package/app/service.test.ts +11 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [0.171.10](https://github.com/nanobpm/nano-workforce/compare/v0.171.9...v0.171.10) (2026-09-01)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* **service:** emit world-restore SHA under harness-read `sha` key ([#696](https://github.com/nanobpm/nano-workforce/issues/696)) ([9794994](https://github.com/nanobpm/nano-workforce/commit/97949948913087c84f79bf0fcaa283c85a7cab0e)), closes [#324](https://github.com/nanobpm/nano-workforce/issues/324) [#695](https://github.com/nanobpm/nano-workforce/issues/695)
|
|
6
|
+
|
|
1
7
|
## [0.171.9](https://github.com/nanobpm/nano-workforce/compare/v0.171.8...v0.171.9) (2026-09-01)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
package/app/contracts.ts
CHANGED
|
@@ -384,9 +384,9 @@ export const WIRE_CONTRACTS = {
|
|
|
384
384
|
name: "io.nanobpm.agentTask.repository",
|
|
385
385
|
owner: "app/repoEnvelope.ts",
|
|
386
386
|
semantics:
|
|
387
|
-
"Repo-provisioning envelope the app emits as a `createInstance` process variable (`repoEnvelopeVars`, app/repoEnvelope.ts) and the c8ctl worker harness consumes to provision an isolated clone — instead of the agent inheriting the worker's launch dir (issue #684). `ref` is the branch checked out: the PR HEAD branch on the PR-based paths (review-round / fix-ci / rebase), or — on the PRE-PR implementation path (feature.bpmn / plan-fanout's `implement-cell`, issue #684; the delivery-graph runner's agent cells, issue #686) — the BASE branch, off which the harness cuts a new feature branch named by the optional `branch.create` (the deterministic `feat/<task.id>`, emitted only for a single-task feature run; the epic seed AND the delivery-graph run-root seed omit it so each fan-out slice's agent branches per node/MI child). Beyond `{provider,url,ref}`, it carries clone-shaping fields for large monorepos (issue #287): `singleBranch:true` + `filter:\"blob:none\"` (a branch-scoped, blobless partial clone — trees fetched up-front, blobs lazily, no `--depth 1` so the merge-base/3-dot diff stays valid) and an optional `baseRef` (the PR base branch, emitted only when resolvable, so the harness fetches its tip and keeps `origin/<base>` reachable). World-restore (issue #324, ADR 0062 Slice 4/5): an optional `
|
|
387
|
+
"Repo-provisioning envelope the app emits as a `createInstance` process variable (`repoEnvelopeVars`, app/repoEnvelope.ts) and the c8ctl worker harness consumes to provision an isolated clone — instead of the agent inheriting the worker's launch dir (issue #684). `ref` is the branch checked out: the PR HEAD branch on the PR-based paths (review-round / fix-ci / rebase), or — on the PRE-PR implementation path (feature.bpmn / plan-fanout's `implement-cell`, issue #684; the delivery-graph runner's agent cells, issue #686) — the BASE branch, off which the harness cuts a new feature branch named by the optional `branch.create` (the deterministic `feat/<task.id>`, emitted only for a single-task feature run; the epic seed AND the delivery-graph run-root seed omit it so each fan-out slice's agent branches per node/MI child). Beyond `{provider,url,ref}`, it carries clone-shaping fields for large monorepos (issue #287): `singleBranch:true` + `filter:\"blob:none\"` (a branch-scoped, blobless partial clone — trees fetched up-front, blobs lazily, no `--depth 1` so the merge-base/3-dot diff stays valid) and an optional `baseRef` (the PR base branch, emitted only when resolvable, so the harness fetches its tip and keeps `origin/<base>` reachable). World-restore (issue #324, ADR 0062 Slice 4/5): an optional `sha` — the last durable push-checkpoint — is emitted so a REPLACEMENT activation on a fresh worktree reconstructs the tree to the EXACT pushed SHA (inverting the round's `git push` into `git fetch && git checkout <sha>`), omitted when the PR has no checkpoint yet. The field is named `sha` because that is the field the c8ctl harness's `provisionRepo` reads to drive the checkout — an earlier `commitSha` key was a silent no-op (issue #695). Gated on c8ctl provisioner support (jwulf/c8ctl-plugin-nano#91).",
|
|
388
388
|
shape:
|
|
389
|
-
'{ provider: "github", url: string, ref: string, singleBranch: true, filter: "blob:none", baseRef?: string,
|
|
389
|
+
'{ provider: "github", url: string, ref: string, singleBranch: true, filter: "blob:none", baseRef?: string, sha?: string, branch?: { create: string } }',
|
|
390
390
|
},
|
|
391
391
|
"epicSet.submit": {
|
|
392
392
|
category: "wire",
|
package/app/repoEnvelope.ts
CHANGED
|
@@ -36,10 +36,12 @@ const AGENT_TASK_NS = "io.nanobpm.agentTask";
|
|
|
36
36
|
* that base reachable for the diff.
|
|
37
37
|
*
|
|
38
38
|
* World-restore (issue #324, ADR 0062 Slice 4/5): when a PR already has a durable push-checkpoint,
|
|
39
|
-
*
|
|
40
|
-
* reconstructs the working tree to the EXACT pushed SHA — the inversion of the
|
|
41
|
-
* `git push` into an inbound `git fetch && git checkout <sha>` — rather than to a
|
|
42
|
-
* have moved.
|
|
39
|
+
* the last pushed SHA is emitted under the `sha` key so a REPLACEMENT activation (a fresh worktree
|
|
40
|
+
* after a lease loss) reconstructs the working tree to the EXACT pushed SHA — the inversion of the
|
|
41
|
+
* round's outbound `git push` into an inbound `git fetch && git checkout <sha>` — rather than to a
|
|
42
|
+
* branch tip that may have moved. The key is `sha` (not `commitSha`) because that is the field the
|
|
43
|
+
* c8ctl worker harness actually reads to drive the checkout; the earlier `commitSha` key was a silent
|
|
44
|
+
* no-op (issue #695). Omitted (no key) when the PR has no checkpoint yet, so a first activation clones the
|
|
43
45
|
* head branch normally.
|
|
44
46
|
*
|
|
45
47
|
* Pre-PR provisioning (issue #684): the implementation path (feature.bpmn / plan-fanout's
|
|
@@ -80,13 +82,16 @@ export function repoEnvelopeVars(
|
|
|
80
82
|
// single-branch head, keeping `origin/<base>` reachable for the diff. Omitted when unknown.
|
|
81
83
|
...(baseRef ? { baseRef } : {}),
|
|
82
84
|
// World-restore (issue #324): the last pushed SHA a replacement activation reconstructs the
|
|
83
|
-
// working tree to (inverting the round's push into a fetch+checkout).
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
85
|
+
// working tree to (inverting the round's push into a fetch+checkout). Emitted under the key
|
|
86
|
+
// `sha` — the field the c8ctl worker harness actually reads (`provisionRepo` normalizes
|
|
87
|
+
// `repo.sha` and drives `git fetch origin <sha>` + `git checkout --detach <sha>` off it); a
|
|
88
|
+
// prior `commitSha` key was a silent no-op the harness never read (issue #695). Only emitted
|
|
89
|
+
// when the value is a well-formed 40-hex commit SHA: it is forwarded to the harness as an
|
|
90
|
+
// EXACT checkout target, so a non-SHA ref or a whitespace-tainted value could reconstruct to
|
|
91
|
+
// an unintended ref (a moved branch tip) or fail provisioning. A malformed value degrades to
|
|
87
92
|
// omission — the harness then clones the head branch tip, the pre-#324 behaviour. Omitted too
|
|
88
93
|
// when the PR has no durable push-checkpoint yet.
|
|
89
|
-
...(isCommitSha(commitSha) ? { commitSha } : {}),
|
|
94
|
+
...(isCommitSha(commitSha) ? { sha: commitSha } : {}),
|
|
90
95
|
// Pre-PR provisioning (issue #684): the implementation path (feature.bpmn / plan-fanout's
|
|
91
96
|
// implement-cell) dispatches its agent BEFORE any PR exists, so `ref` is the BASE branch, not a
|
|
92
97
|
// head. `branchCreate` asks the harness to cut the deterministic `feat/<task.id>` feature branch
|
package/app/service.test.ts
CHANGED
|
@@ -534,20 +534,24 @@ test("repoEnvelopeVars emits nothing for a malformed repo (not owner/repo)", ()
|
|
|
534
534
|
}
|
|
535
535
|
});
|
|
536
536
|
|
|
537
|
-
test("repoEnvelopeVars emits
|
|
537
|
+
test("repoEnvelopeVars emits the push-checkpoint under the harness-read `sha` key, only for a well-formed 40-hex SHA (world-restore, #324/#695)", () => {
|
|
538
538
|
const sha = "77ee0993cc6ad4493da0f7551212ef16722135db";
|
|
539
539
|
const env = (repoEnvelopeVars("owner/repo", "feat/x", "main", sha) as any)["io.nanobpm.agentTask"];
|
|
540
|
-
|
|
541
|
-
//
|
|
542
|
-
//
|
|
543
|
-
|
|
540
|
+
// The emitted key MUST be `sha` — the field the c8ctl harness `provisionRepo` reads to drive
|
|
541
|
+
// `git fetch origin <sha>` + `git checkout --detach <sha>`. A prior `commitSha` key was a silent
|
|
542
|
+
// no-op the harness never read (issue #695), so guard the exact wire name here, not just presence.
|
|
543
|
+
assertEquals(env.repository.sha, sha, "a valid 40-hex SHA is threaded through under the harness-read `sha` key");
|
|
544
|
+
assertEquals("commitSha" in env.repository, false, "the retired `commitSha` key must never be emitted (silent no-op, #695)");
|
|
545
|
+
// A non-SHA ref, an abbreviated SHA, or a whitespace-tainted value is dropped (no `sha` key): it is
|
|
546
|
+
// forwarded to the harness as an EXACT checkout target, so a bad value could reconstruct to a moved
|
|
547
|
+
// branch tip or fail provisioning. Omission degrades to the pre-#324 head-branch-tip clone.
|
|
544
548
|
for (const bad of ["main", "feat/x", "77ee099", `${sha} `, ` ${sha}`, `${sha}\n`, "z".repeat(40), `${sha}0`, ""]) {
|
|
545
549
|
const r = (repoEnvelopeVars("owner/repo", "feat/x", "main", bad) as any)["io.nanobpm.agentTask"].repository;
|
|
546
|
-
assertEquals("
|
|
550
|
+
assertEquals("sha" in r, false, `expected no sha for "${JSON.stringify(bad)}"`);
|
|
547
551
|
}
|
|
548
552
|
// Omitted entirely when there is no checkpoint SHA at all (the common first-activation case).
|
|
549
553
|
const none = (repoEnvelopeVars("owner/repo", "feat/x", "main") as any)["io.nanobpm.agentTask"].repository;
|
|
550
|
-
assertEquals("
|
|
554
|
+
assertEquals("sha" in none, false);
|
|
551
555
|
});
|
|
552
556
|
|
|
553
557
|
// Pre-PR provisioning (issue #684): the implementation path has no head branch yet, so it passes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/nano-workforce",
|
|
3
|
-
"version": "0.171.
|
|
3
|
+
"version": "0.171.10",
|
|
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",
|