c8ctl-plugin-nano 1.61.0 → 1.61.2

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/README.md CHANGED
@@ -728,7 +728,7 @@ only `latest`, so the key is the reproducibility handle).
728
728
 
729
729
  On completion the plugin writes an **output envelope** back under
730
730
  `io.nanobpm.agentResult` (`{schemaVersion, status, sandbox, image, output, truncated, stderrTruncated, exitCode, signal, error, promptResourceKey?}`). When a repository was
731
- provisioned (below) it also carries `{repository, branch, baseSha, headSha, commits[], pushed, pushError?, gitError?, pr?}`.
731
+ provisioned (below) it also carries `{repository, branch, baseSha, headSha, commits[], pushed, pushError?, pushFailed?, strandedCommits?, branchMismatch?, scanError?, gitError?, pr?}`. `pushFailed` is the explicit "push failed" flag — set for a non-zero `git push` **whose result could not be confirmed as landed at the remote** (a non-fast-forward rejection, or an auth, hook, or network error), not only a server rejection. **`pushFailed` does not by itself prove a remote push was attempted or rejected:** `finalizeGit` also sets it (with `strandedCommits` and a `branchMismatch` `{expected, actual}`) when it *refuses to push at all* because the harness moved HEAD off the provisioned work branch — or left commits abandoned on another local branch — so pushing the work branch would publish stale work and strand those commits (no `git push` runs, so `pushError` is absent in that case). As a guard against false strands, a non-zero push is re-checked with `ls-remote`: if `origin/<branch>` already points at `headSha` — or the remote tip is a **descendant** of `headSha` (another actor pushed a further commit after ours landed) — the push is treated as a transport hiccup that landed after the ref was accepted (`pushed: true`, no `pushFailed`/`pushError`); otherwise `pushFailed` is set and `strandedCommits` lists the SHAs of the new commits left UNPUSHED in the throwaway workspace — together they are the recovery handle for a failed push, so consumers must not treat `pushed: false` alone as the only signal. On such a failure (a rejected push **or** a branch-mismatch refusal) the throwaway workspace is preserved **best-effort** (even under the default `--keep-runs=false`) so those SHAs stay recoverable, but this is a *recovery window, not a durable archive*: the run-directory reaper still ages it out by mtime and worker shutdown removes the namespace — copy the stranded commits out promptly (or run with `--keep-runs`). **`scanError`** flags a **best-effort/PARTIAL** `strandedCommits` list: a commit or remote-reachability scan did **not** complete, so a consumer should recover the whole preserved workspace rather than trust the list exactly. It arises two ways: (a) a **pre-push** commit-enumeration scan failed, so `finalizeGit` refused to push on an incomplete graph — **no push was attempted** (`pushError` is absent) and `strandedCommits` is a partial surfacing; or (b) a push was **ATTEMPTED and rejected** but the remote-reachability FILTER that trims already-published commits from the strand set itself failed — so `scanError` is present **alongside** `pushError`, marking the rejected-push strand list as best-effort (it may falsely include an already-landed commit). Either way `pushFailed` stays authoritative; `scanError` is how a consumer knows the strand list is inexact (recover the whole workspace) versus an exact list (a rejected push with no `scanError`).
732
732
 
733
733
  **Git provisioning (host).** When `--sandbox none` (the default) and the envelope
734
734
  carries a `repository.url`, the plugin provisions a workspace on the host around
@@ -764,7 +764,17 @@ the harness:
764
764
  per envelope (default 120s, or the `--clone-timeout` worker flag) as a backstop
765
765
  for repos big enough to approach the cap even when shallow; a timeout is now
766
766
  reported *as a timeout* rather than an opaque `exit 128`;
767
- 3. create `branch.create` (if set) off that target;
767
+ 3. create `branch.create` (if set) off that target — or, when `branch.create` is
768
+ **absent** (or names the effective base itself) **and push is enabled**, cut a
769
+ generated fallback work branch `nano/agent-work/<base>-<runId>` so commits are
770
+ never made directly on the base branch; the branch actually used (configured or
771
+ generated) rides back in the result envelope (`branch`) and is exported to the
772
+ harness as `AGENT_REPO_BRANCH`. **Exception — a detached tag/SHA checkout with
773
+ no `branch.create`:** with no symbolic branch to name a fallback from, it stays
774
+ `branch: null` and its commits are **not** pushed even when push is enabled —
775
+ this holds for *any* detached no-`branch.create` checkout, **including** when a
776
+ `branch.base` is configured (the base name is not used to synthesize a fallback
777
+ here). Supply `branch.create` to publish work committed off a tag/SHA base;
768
778
  4. set a **committer identity** on the workspace, preferring the operator's own
769
779
  (`GIT_AUTHOR_*` env → global `git config user.name/email` → the
770
780
  `gh`-authenticated GitHub user), and only falling back to `nano-agent` when
@@ -793,10 +803,36 @@ config is neutralized (`GIT_CONFIG_GLOBAL` → the platform null device,
793
803
  or `url.*.insteadOf` can't silently inject operator credentials. (An **SSH**
794
804
  remote — `git@…`/`ssh://…` — can still authenticate via the host's SSH
795
805
  agent/config; use HTTPS URLs if you need a guaranteed-anonymous clone.)
796
- Token-backed jobs keep global config (e.g. `http.proxy`). A push failure is
797
- reported as `pushError` (the job still completes) so a later BPMN step can drive
798
- the merge; a clone/checkout failure sheds the job (retryable). Workspaces are
799
- deleted after each job (keep them with `--keep-runs`).
806
+ Token-backed jobs keep global config (e.g. `http.proxy`). **`pushFailed` is the
807
+ authoritative "work is stranded, workspace preserved" flag** (the job still
808
+ completes, so a later BPMN step can drive the merge); a clone/checkout failure
809
+ sheds the job (retryable). `pushError` is present **only** for a push that was
810
+ ATTEMPTED and rejected/unconfirmed — when `finalizeGit` refuses to push at all (a
811
+ branch mismatch, or a partial/incomplete commit scan), it sets `pushFailed`
812
+ (with `branchMismatch` or `scanError`) but **no** `pushError`, so consumers must
813
+ key recovery off `pushFailed`, not `pushError`. **`scanError` flags a
814
+ best-effort/PARTIAL `strandedCommits` list** — a commit/reachability scan did not
815
+ complete, so the list may omit or (after a rejected push) falsely include commits
816
+ and a consumer should recover the whole preserved workspace rather than trust the
817
+ list exactly. It arises two ways: (a) a pre-push commit-enumeration scan failed, so
818
+ `finalizeGit` refused to push on an incomplete graph (**no** `git push` ran, so
819
+ `pushError` is absent); or (b) a push was ATTEMPTED and rejected but the
820
+ remote-reachability FILTER that trims already-published commits from the strand set
821
+ itself failed — so `scanError` is present **alongside** `pushError`, marking the
822
+ otherwise-complete rejected-push strand list as best-effort. Either way `pushFailed`
823
+ stays authoritative. Workspaces are
824
+ deleted after each job (keep them with `--keep-runs`) — **except** a job whose
825
+ `git push` **could not be confirmed as landed** (a non-fast-forward rejection, or
826
+ an auth/hook/network error whose `ls-remote` re-check did not find `origin/<branch>`
827
+ at or ahead of `headSha` — `pushFailed` + `pushError`), **or** one where
828
+ `finalizeGit` refused to push (no `git push` ran, so `pushError` is absent)
829
+ because HEAD moved off the provisioned work branch — or left commits on another
830
+ local branch or an abandoned detached HEAD reachable only via the reflog (a
831
+ `branchMismatch` strand) — **or** because a critical local commit scan did not
832
+ complete (a `scanError` strand, with a best-effort PARTIAL `strandedCommits`),
833
+ whose workspace is preserved best-effort so its
834
+ `strandedCommits` stay recoverable; that preservation is still age-gated by the
835
+ reaper and cleared on worker shutdown, so recover the SHAs promptly.
800
836
 
801
837
  ```bash
802
838
  # The harness sees a cloned repo at $AGENT_WORKSPACE; branch/push/PR are handled for it.