@bluewombat/slots 0.9.0 → 0.10.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.
@@ -23,6 +23,12 @@ import {
23
23
  // fast-forward has been written to by somebody else, and that is a person's
24
24
  // problem, not a merge to attempt.
25
25
  //
26
+ // A Feature's line cut from the final branch that has no commit of its own is
27
+ // only a stale copy of it: when `--base` names that branch and the line is an
28
+ // ancestor of it, the line is fast-forwarded to it, here and on the remote, so
29
+ // a Ticket starts from the code as it now is. A line with commits of its own
30
+ // is left alone.
31
+ //
26
32
  // The remote is `origin`, like the git Publisher's. Takes no option.
27
33
 
28
34
  const cwd = process.cwd();
@@ -53,6 +59,34 @@ if (fetched.status !== 0) {
53
59
  process.exit(0);
54
60
  }
55
61
 
62
+ const base = take(process.argv, "--base");
63
+ let goal = `origin/${target}`;
64
+ if (base !== undefined && base.trim().length > 0 && base !== target) {
65
+ const fetchedBase = git(["fetch", "origin", `+refs/heads/${base}:refs/remotes/origin/${base}`]);
66
+ if (fetchedBase.status !== 0) {
67
+ emitRefreshRefusal(
68
+ `Cannot fetch origin/${base} from ${cwd}: ${fetchedBase.stderr.trim().slice(-500)}`,
69
+ );
70
+ process.exit(0);
71
+ }
72
+ const behindBase =
73
+ git(["merge-base", "--is-ancestor", `origin/${target}`, `origin/${base}`]).status === 0;
74
+ if (
75
+ behindBase &&
76
+ git(["rev-parse", `origin/${target}`]).stdout !== git(["rev-parse", `origin/${base}`]).stdout
77
+ ) {
78
+ // A plain push: never forced, so it only ever adds history.
79
+ const pushed = git(["push", "origin", `refs/remotes/origin/${base}:refs/heads/${target}`]);
80
+ if (pushed.status !== 0) {
81
+ emitRefreshRefusal(
82
+ `Cannot move origin/${target} up to origin/${base}: ${pushed.stderr.trim().slice(-500)}`,
83
+ );
84
+ process.exit(0);
85
+ }
86
+ goal = `origin/${base}`;
87
+ }
88
+ }
89
+
56
90
  const current = git(["symbolic-ref", "--short", "HEAD"]);
57
91
  if (current.status !== 0 || current.stdout.trim() !== target) {
58
92
  const exists = git(["rev-parse", "--verify", "--quiet", `refs/heads/${target}`]).status === 0;
@@ -65,7 +99,7 @@ if (current.status !== 0 || current.stdout.trim() !== target) {
65
99
  }
66
100
  }
67
101
 
68
- const merged = git(["merge", "--ff-only", `origin/${target}`]);
102
+ const merged = git(["merge", "--ff-only", goal]);
69
103
  if (merged.status !== 0) {
70
104
  // The one way a copy stops fast-forwarding is a commit made into it that
71
105
  // the reference does not have. The copy is the system's, not a person's:
@@ -15,7 +15,7 @@ import {
15
15
  } from "@bluewombat/slot-kit";
16
16
  import { attemptOf } from "../lib/fill.mjs";
17
17
 
18
- const NAME = "assembly fix";
18
+ const NAME = "ticket fix";
19
19
 
20
20
  const DEFAULT_TEMPLATE = `You are a coding agent working unattended in a checkout of the project.
21
21
 
@@ -60,7 +60,7 @@ A person reads that line and decides. It is a correct answer, not a failure.
60
60
 
61
61
  const report = (take(process.argv, "--report") ?? "").trim();
62
62
  if (report.length === 0) {
63
- emitFailure("fail-blocking", "The assembly fix command needs --report.");
63
+ emitFailure("fail-blocking", "The ticket fix command needs --report.");
64
64
  process.exit(1);
65
65
  }
66
66
 
@@ -15,7 +15,7 @@ import {
15
15
  } from "@bluewombat/slot-kit";
16
16
  import { attemptOf } from "../lib/fill.mjs";
17
17
 
18
- const NAME = "assembly validate";
18
+ const NAME = "ticket validate";
19
19
 
20
20
  const DEFAULT_TEMPLATE = `You are a coding agent working unattended in a checkout of the project.
21
21
 
@@ -67,7 +67,7 @@ const report = (take(process.argv, "--report") ?? "").trim();
67
67
  if (report.length > 0) {
68
68
  emitFailure(
69
69
  "fail-blocking",
70
- "assembly.validate is a read-only review; it does not take --report — that is assembly.fix's job.",
70
+ "ticket.validate is a read-only review; it does not take --report — that is ticket.fix's job.",
71
71
  );
72
72
  process.exit(1);
73
73
  }
@@ -149,7 +149,7 @@ function answer(run, bin, name) {
149
149
  // next round gets the same prompt again.
150
150
  emitFailure(
151
151
  "fail-retryable",
152
- `assembly.validate found no MASON_VERDICT: line in the answer: ${
152
+ `ticket.validate found no MASON_VERDICT: line in the answer: ${
153
153
  said.length > 0 ? said.slice(0, 500) : "(no answer)"
154
154
  }`,
155
155
  );
@@ -160,9 +160,7 @@ function answer(run, bin, name) {
160
160
  const why = verdict.slice(refused[0].length).trim();
161
161
  emitFailure(
162
162
  "fail-retryable",
163
- why.length > 0
164
- ? why
165
- : "assembly.validate refused the assembled ticket, with no reason given.",
163
+ why.length > 0 ? why : "ticket.validate refused the assembled ticket, with no reason given.",
166
164
  );
167
165
  process.exit(1);
168
166
  }
@@ -171,7 +169,7 @@ function answer(run, bin, name) {
171
169
  }
172
170
  emitFailure(
173
171
  "fail-retryable",
174
- `assembly.validate's MASON_VERDICT: line was neither VALIDATED nor REFUSED: ${verdict.slice(0, 500)}`,
172
+ `ticket.validate's MASON_VERDICT: line was neither VALIDATED nor REFUSED: ${verdict.slice(0, 500)}`,
175
173
  );
176
174
  process.exit(1);
177
175
  }