@botbuddy/cli 1.6.1 → 1.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbuddy/cli",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "description": "BotBuddy — Swarm coordination CLI for multi-agent workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1 @@
1
+ {"schema_version":1,"source_version":"1.6.1","source_identity":"9cc6a78baeb0da589214386daad729a2e8909936a2407da801777f9902e8667f"}
package/src/wait-core.mjs CHANGED
@@ -228,6 +228,35 @@ const VALIDATORS = {
228
228
  }
229
229
  return { repo };
230
230
  },
231
+ // BOT-1458 — staging-release: wake when the first staging release train whose
232
+ // head commit CONTAINS this PR's merge commit reaches a terminal conclusion
233
+ // (success OR failure). Unlike `staging-green` this is PR-scoped and reports
234
+ // the train's outcome, never a health verdict. Inclusion is decided server-side
235
+ // by git ancestry (GitHub compare API), never by wall-clock ordering, so the
236
+ // condition only names the repo + PR; the relay stamps the resolved
237
+ // merge_commit_sha and workspace at registration. Optional
238
+ // `after_run=<run_id>[:<attempt>]` lets a wait re-armed from a failure receipt
239
+ // skip the run it already saw and park for the NEXT train.
240
+ "staging-release"(p) {
241
+ const { repo, number } = parsePrTarget(p, "staging-release");
242
+ if (number == null) {
243
+ throw new Error("staging-release needs pr=<number> (or pr=<owner/repo#number>)");
244
+ }
245
+ const out = { repo, pr: number };
246
+ if (p.after_run !== undefined) {
247
+ const raw = String(p.after_run).trim();
248
+ const m = /^(\d+)(?::(\d+))?$/.exec(raw);
249
+ if (!m) throw new Error("staging-release after_run must be <run_id>[:<attempt>]");
250
+ out.afterRunId = m[1];
251
+ if (m[2] !== undefined) out.afterRunAttempt = Number(m[2]);
252
+ }
253
+ const allowed = new Set(["repo", "pr", "number", "after_run"]);
254
+ const extras = Object.keys(p).filter((key) => !allowed.has(key));
255
+ if (extras.length > 0) {
256
+ throw new Error(`staging-release accepts only repo=<owner/repo>, pr=<number>, after_run=<run_id>[:<attempt>] (got: ${extras.join(", ")})`);
257
+ }
258
+ return out;
259
+ },
231
260
  // BOT-1247 — unblocked: wake when a ticket's LAST open blocker clears (its
232
261
  // open-blocker count transitions >0 → 0). Level-triggered: a ticket already
233
262
  // unblocked (or never blocked) at registration is granted immediately with
@@ -666,6 +695,27 @@ function conditionMatchesSignal(condition, signal, waitSessionId) {
666
695
  payload.gate_open === false &&
667
696
  payload.operational_status === "healthy";
668
697
  }
698
+ case "staging-release": {
699
+ // BOT-1458: a PR-scoped staging release train reached a terminal conclusion
700
+ // and its head commit contains this PR's merge (`includes_pr:true`, decided
701
+ // server-side by ancestry). The signal is tenant-scoped, so the central
702
+ // guard already fenced foreign workspaces. Subject is `<owner/repo>#<pr>`.
703
+ if (signal.signal_type !== "staging_release") return false;
704
+ const payload = signal.payload ?? {};
705
+ const subject = typeof signal.subject_key === "string" ? signal.subject_key.toLowerCase() : "";
706
+ if (subject !== `${params.repo}#${params.pr}`.toLowerCase()) return false;
707
+ if (payload.includes_pr !== true) return false;
708
+ // AC-9: a wait re-armed with after_run ignores the run it already saw — that
709
+ // run id, and (when an attempt was named) that attempt or lower — so a
710
+ // re-registration after a failure receipt parks for the NEXT train instead
711
+ // of instantly replaying the same failure via `--since`.
712
+ if (params.afterRunId != null && String(payload.workflow_run_id) === String(params.afterRunId)) {
713
+ if (params.afterRunAttempt == null) return false;
714
+ const attempt = Number(payload.workflow_run_attempt);
715
+ if (Number.isFinite(attempt) && attempt <= params.afterRunAttempt) return false;
716
+ }
717
+ return true;
718
+ }
669
719
  case "unblocked": {
670
720
  // Wakes on the ticket_unblocked spine signal for this ticket (the server emits both
671
721
  // the genuine >0→0 transition and the level-triggered `already_unblocked` echo).