@rsaglobaltech/specgate 0.21.0 → 0.22.1

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/README.md +81 -96
  3. package/dist/packages/core/src/domain/BriefCards.js +320 -0
  4. package/dist/packages/core/src/domain/BriefCards.js.map +1 -0
  5. package/dist/packages/core/src/domain/DraftChecklist.js +5 -1
  6. package/dist/packages/core/src/domain/DraftChecklist.js.map +1 -1
  7. package/dist/packages/core/src/domain/QuotedText.js +5 -0
  8. package/dist/packages/core/src/domain/QuotedText.js.map +1 -1
  9. package/dist/packages/core/src/domain/TicketText.js +337 -0
  10. package/dist/packages/core/src/domain/TicketText.js.map +1 -0
  11. package/dist/packages/core/src/infrastructure/ChangeWorkspace.js +7 -3
  12. package/dist/packages/core/src/infrastructure/ChangeWorkspace.js.map +1 -1
  13. package/dist/packages/core/src/infrastructure/IdsAcrossBranches.js +48 -0
  14. package/dist/packages/core/src/infrastructure/IdsAcrossBranches.js.map +1 -0
  15. package/dist/packages/mcp-spec-driven/src/native-tools.js +1 -1
  16. package/dist/packages/mcp-spec-driven/src/native-tools.js.map +1 -1
  17. package/dist/scripts/agents/commands.js +8 -6
  18. package/dist/scripts/agents/commands.js.map +1 -1
  19. package/dist/scripts/agents/contract-file.js +44 -0
  20. package/dist/scripts/agents/contract-file.js.map +1 -1
  21. package/dist/scripts/alm/cli.js +16 -4
  22. package/dist/scripts/alm/cli.js.map +1 -1
  23. package/dist/scripts/alm/port.js +1 -1
  24. package/dist/scripts/alm/port.js.map +1 -1
  25. package/dist/scripts/alm/providers/http-client.js +46 -3
  26. package/dist/scripts/alm/providers/http-client.js.map +1 -1
  27. package/dist/scripts/alm/providers/jira.js +108 -32
  28. package/dist/scripts/alm/providers/jira.js.map +1 -1
  29. package/dist/scripts/cli/commands/alm/PrCommand.js +198 -0
  30. package/dist/scripts/cli/commands/alm/PrCommand.js.map +1 -0
  31. package/dist/scripts/cli/commands/change/ChangeCommand.js +32 -6
  32. package/dist/scripts/cli/commands/change/ChangeCommand.js.map +1 -1
  33. package/dist/scripts/cli/commands/change/TicketCommand.js +657 -0
  34. package/dist/scripts/cli/commands/change/TicketCommand.js.map +1 -0
  35. package/dist/scripts/cli/commands/harness/RunCommand.js +4 -2
  36. package/dist/scripts/cli/commands/harness/RunCommand.js.map +1 -1
  37. package/dist/scripts/cli/commands/project/AdoptCommand.js +58 -0
  38. package/dist/scripts/cli/commands/project/AdoptCommand.js.map +1 -1
  39. package/dist/scripts/cli/commands/project/UpdateCommand.js +4 -0
  40. package/dist/scripts/cli/commands/project/UpdateCommand.js.map +1 -1
  41. package/dist/scripts/cli/commands/quality/CheckCommand.js +12 -0
  42. package/dist/scripts/cli/commands/quality/CheckCommand.js.map +1 -1
  43. package/dist/scripts/cli/commands/spec/DraftCommand.js +240 -27
  44. package/dist/scripts/cli/commands/spec/DraftCommand.js.map +1 -1
  45. package/dist/scripts/cli/commands/spec/ExploreCommand.js +13 -13
  46. package/dist/scripts/cli/commands/spec/ExploreCommand.js.map +1 -1
  47. package/dist/scripts/cli/commands/spec/StatusCommand.js +18 -6
  48. package/dist/scripts/cli/commands/spec/StatusCommand.js.map +1 -1
  49. package/dist/scripts/docs/journey-storyboard.js +67 -44
  50. package/dist/scripts/docs/journey-storyboard.js.map +1 -1
  51. package/dist/scripts/docs/nav.js +156 -139
  52. package/dist/scripts/docs/nav.js.map +1 -1
  53. package/dist/scripts/docs/record-journey.js +139 -49
  54. package/dist/scripts/docs/record-journey.js.map +1 -1
  55. package/dist/scripts/docs/terminal-storyboard.js +15 -11
  56. package/dist/scripts/docs/terminal-storyboard.js.map +1 -1
  57. package/dist/scripts/draft/brief-check.js +34 -0
  58. package/dist/scripts/draft/brief-check.js.map +1 -0
  59. package/dist/scripts/draft/brief-setup.js +182 -17
  60. package/dist/scripts/draft/brief-setup.js.map +1 -1
  61. package/dist/scripts/draft/drafting-guide.js +43 -1
  62. package/dist/scripts/draft/drafting-guide.js.map +1 -1
  63. package/dist/scripts/draft/ticket-guide.js +129 -0
  64. package/dist/scripts/draft/ticket-guide.js.map +1 -0
  65. package/dist/scripts/lib/surface.js +2 -0
  66. package/dist/scripts/lib/surface.js.map +1 -1
  67. package/dist/scripts/lib/ticket-keys.js +140 -0
  68. package/dist/scripts/lib/ticket-keys.js.map +1 -0
  69. package/dist/scripts/propose.js +8 -2
  70. package/dist/scripts/propose.js.map +1 -1
  71. package/dist/scripts/vcs/github-pr.js +88 -0
  72. package/dist/scripts/vcs/github-pr.js.map +1 -0
  73. package/package.json +1 -1
  74. package/templates/base/AGENTS.md.tpl +6 -5
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ /**
3
+ * The ticket key travels with the work (ADR-0039): a Git host linked to Jira
4
+ * shows a ticket's branch, commits and pull request only when they name its
5
+ * key. On a ticket's branch, a commit that does not is invisible to the
6
+ * ticket, so `check` says which.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.baseFor = baseFor;
43
+ exports.ticketKeyFindings = ticketKeyFindings;
44
+ exports.ticketKeyOf = ticketKeyOf;
45
+ exports.harnessCommitMessage = harnessCommitMessage;
46
+ const node_child_process_1 = require("node:child_process");
47
+ const fs = __importStar(require("node:fs"));
48
+ const path = __importStar(require("node:path"));
49
+ const diagnostics_1 = require("./diagnostics");
50
+ const base_ids_1 = require("./base-ids");
51
+ const GitSafety_1 = require("../../packages/core/src/domain/GitSafety");
52
+ /** `feature/KAN-13-register-vehicle-entry` → `KAN-13`. */
53
+ const TICKET_BRANCH = /^(?:feature|bugfix|hotfix)\/([A-Z][A-Z0-9_]+-\d+)(?:-|$)/;
54
+ function git(dir, args) {
55
+ return (0, node_child_process_1.spawnSync)("git", ["-C", dir, ...args], { encoding: "utf8" });
56
+ }
57
+ /** The base: `--base`, SPECGATE_BASE or origin's default, else develop, main, master. */
58
+ function baseFor(dir, given) {
59
+ const resolved = (0, base_ids_1.resolveBase)(dir, given);
60
+ if (resolved) {
61
+ // Throws for a ref git would read as an option: a base that cannot be
62
+ // used is an error, never "no base" — that would mean HEAD.
63
+ (0, GitSafety_1.assertSafeGitRef)(resolved, "base");
64
+ return resolved;
65
+ }
66
+ for (const name of ["develop", "main", "master"]) {
67
+ if (git(dir, ["rev-parse", "--verify", "--quiet", `refs/heads/${name}`]).status === 0)
68
+ return name;
69
+ }
70
+ return null;
71
+ }
72
+ function ticketKeyFindings(dir, given) {
73
+ const branch = git(dir, ["rev-parse", "--abbrev-ref", "HEAD"]);
74
+ if (branch.status !== 0)
75
+ return [];
76
+ const key = TICKET_BRANCH.exec(branch.stdout.trim())?.[1];
77
+ if (!key)
78
+ return [];
79
+ let base;
80
+ try {
81
+ base = baseFor(dir, given);
82
+ }
83
+ catch {
84
+ return [];
85
+ }
86
+ if (!base || base === branch.stdout.trim())
87
+ return [];
88
+ const log = git(dir, ["log", `${base}..HEAD`, "--format=%h%x09%s"]);
89
+ if (log.status !== 0)
90
+ return [];
91
+ const named = new RegExp(`(^|[^A-Z0-9_-])${key}(?![0-9])`);
92
+ return log.stdout
93
+ .split("\n")
94
+ .filter(Boolean)
95
+ .map((line) => line.split("\t"))
96
+ .filter(([, subject]) => !named.test(subject ?? ""))
97
+ .map(([sha, subject]) => (0, diagnostics_1.warning)("ticket_key_missing", `Commit ${sha} "${subject}" does not name ${key}.`, {
98
+ target: sha,
99
+ fix: `Reword it to start with ${key} (git commit --amend for the last one), so Jira shows it in ${key}.`,
100
+ }));
101
+ }
102
+ const CHANGES = path.join("docs", "specs", "changes");
103
+ const JIRA_KEY = /^[A-Z][A-Z0-9_]+-\d+$/;
104
+ function specFiles(dir) {
105
+ if (!fs.existsSync(dir))
106
+ return [];
107
+ return fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) => {
108
+ const full = path.join(dir, e.name);
109
+ return e.isDirectory() ? specFiles(full) : e.name === "spec.md" ? [full] : [];
110
+ });
111
+ }
112
+ /** The ticket of the open change whose delta holds `requirement`, or null (REQ-156). */
113
+ function ticketKeyOf(projectDir, requirement) {
114
+ const changes = path.join(projectDir, CHANGES);
115
+ if (!fs.existsSync(changes))
116
+ return null;
117
+ const heading = new RegExp(`^###\\s+Requirement:\\s+${requirement}(?![0-9])`, "m");
118
+ for (const e of fs.readdirSync(changes, { withFileTypes: true })) {
119
+ if (!e.isDirectory() || e.name === "archive" || e.name.startsWith("."))
120
+ continue;
121
+ const dir = path.join(changes, e.name);
122
+ if (!specFiles(path.join(dir, "specs")).some((f) => heading.test(fs.readFileSync(f, "utf8"))))
123
+ continue;
124
+ const yaml = path.join(dir, "change.yaml");
125
+ const key = fs.existsSync(yaml)
126
+ ? /^ticket:\s*(\S+)\s*$/m.exec(fs.readFileSync(yaml, "utf8"))?.[1]
127
+ : undefined;
128
+ return key && JIRA_KEY.test(key) ? key : null;
129
+ }
130
+ return null;
131
+ }
132
+ /** The harness's commit for a requirement: the ticket key first when it has one. */
133
+ function harnessCommitMessage(projectDir, requirement, attempt, maxAttempts) {
134
+ const key = ticketKeyOf(projectDir, requirement);
135
+ const subject = key
136
+ ? `${key} ${requirement}: implement via specgate harness`
137
+ : `feat(${requirement}): implement via specgate harness`;
138
+ return `${subject}\n\nAttempt ${attempt}/${maxAttempts}.`;
139
+ }
140
+ //# sourceMappingURL=ticket-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-keys.js","sourceRoot":"","sources":["../../../scripts/lib/ticket-keys.ts"],"names":[],"mappings":"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"}
@@ -4,14 +4,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.propose = propose;
5
5
  const ChangeCommand_1 = require("./cli/commands/change/ChangeCommand");
6
6
  const DraftCommand_1 = require("./cli/commands/spec/DraftCommand");
7
+ const TicketCommand_1 = require("./cli/commands/change/TicketCommand");
7
8
  /**
8
9
  * `specgate propose` — the first of the daily verbs (ADR-0035). An idea opens
9
10
  * a change, as `change new` does; `--from-brief` drafts the next module of
10
- * docs/brief.md, as `draft` does. One engine each, two ways in.
11
+ * docs/brief.md, as `draft` does; `--from-ticket` takes a Jira ticket as the
12
+ * source text instead (ADR-0039).
11
13
  */
12
14
  function propose(argv) {
15
+ if (argv.includes("--from-ticket")) {
16
+ void new TicketCommand_1.TicketCommand(argv).execute();
17
+ return;
18
+ }
13
19
  if (argv.includes("--from-brief")) {
14
- new DraftCommand_1.DraftCommand(argv.filter((a) => a !== "--from-brief")).execute();
20
+ new DraftCommand_1.DraftCommand(argv.filter((a) => a !== "--from-brief"), "propose --from-brief").execute();
15
21
  return;
16
22
  }
17
23
  new ChangeCommand_1.CliCommand(["new", ...argv], "propose").execute();
@@ -1 +1 @@
1
- {"version":3,"file":"propose.js","sourceRoot":"","sources":["../../scripts/propose.ts"],"names":[],"mappings":";;;AASA,0BAMC;AAdD,uEAAiE;AACjE,mEAAgE;AAEhE;;;;GAIG;AACH,SAAgB,OAAO,CAAC,IAAc;IACpC,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QAClC,IAAI,2BAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACrE,OAAO;IACT,CAAC;IACD,IAAI,0BAAU,CAAC,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;AACxD,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM;IAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"propose.js","sourceRoot":"","sources":["../../scripts/propose.ts"],"names":[],"mappings":";;;AAWA,0BAaC;AAvBD,uEAAiE;AACjE,mEAAgE;AAChE,uEAAoE;AAEpE;;;;;GAKG;AACH,SAAgB,OAAO,CAAC,IAAc;IACpC,IAAI,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QACnC,KAAK,IAAI,6BAAa,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QACvC,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QAClC,IAAI,2BAAY,CACd,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,cAAc,CAAC,EACxC,sBAAsB,CACvB,CAAC,OAAO,EAAE,CAAC;QACZ,OAAO;IACT,CAAC;IACD,IAAI,0BAAU,CAAC,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;AACxD,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM;IAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * One pull request per ticket, on GitHub (ADR-0039 §4).
4
+ *
5
+ * The pull request belongs to the repository, not to the ALM: this lives
6
+ * outside `scripts/alm/`, whose port still only creates, reads, closes and
7
+ * lists issues. It opens or updates a pull request, and nothing else — no
8
+ * merge, no close, no branch deletion. The token comes from the environment
9
+ * variable `pr.token_env` names and is never printed.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.prConfig = prConfig;
13
+ exports.mergeBody = mergeBody;
14
+ exports.openOrUpdatePr = openOrUpdatePr;
15
+ const http_client_1 = require("../alm/providers/http-client");
16
+ /** The `pr:` block of `alm.config.yaml`, checked; throws with a fix. */
17
+ function prConfig(raw) {
18
+ const pr = (raw && typeof raw === "object" ? raw : {});
19
+ const provider = String(pr.provider ?? "github");
20
+ if (provider !== "github") {
21
+ throw new Error(`pr.provider '${provider}' is not supported.\nFix: set pr.provider: github.`);
22
+ }
23
+ const repo = String(pr.repo ?? "");
24
+ if (!/^[\w.-]+\/[\w.-]+$/.test(repo)) {
25
+ throw new Error("alm.config.yaml has no pr.repo.\nFix: add\npr:\n provider: github\n repo: <owner>/<repo>\n base: develop\n token_env: GITHUB_TOKEN");
26
+ }
27
+ const apiUrl = String(pr.base_url ?? "https://api.github.com").replace(/\/$/, "");
28
+ (0, http_client_1.assertSecureUrl)(apiUrl, "pr.base_url");
29
+ return {
30
+ repo,
31
+ base: String(pr.base ?? "develop"),
32
+ tokenEnv: String(pr.token_env ?? "GITHUB_TOKEN"),
33
+ apiUrl,
34
+ };
35
+ }
36
+ const BLOCK = /<!-- specgate:pr [^>]*-->[\s\S]*?<!-- \/specgate:pr -->/;
37
+ /** The block's opening marker, its closer deleted: everything after it was ours. */
38
+ const OPENED = /<!-- specgate:pr [^>]*-->[\s\S]*$/;
39
+ /** The description with Specgate's block replaced, or added after what is there. */
40
+ function mergeBody(current, block) {
41
+ if (BLOCK.test(current))
42
+ return current.replace(BLOCK, () => block);
43
+ if (OPENED.test(current))
44
+ return current.replace(OPENED, () => block);
45
+ return current.trim() ? `${current.trimEnd()}\n\n${block}` : block;
46
+ }
47
+ async function openOrUpdatePr(cfg, pr, fetchImpl = globalThis.fetch) {
48
+ const token = process.env[cfg.tokenEnv];
49
+ if (!token) {
50
+ throw new Error(`Environment variable ${cfg.tokenEnv} is not set.\n` +
51
+ "Fix: export a token that may write pull requests to this repository " +
52
+ "(a fine-grained token for this repository with Pull requests: read and write; pushing uses your git credentials).");
53
+ }
54
+ const headers = {
55
+ Authorization: `Bearer ${token}`,
56
+ Accept: "application/vnd.github+json",
57
+ "Content-Type": "application/json",
58
+ "X-GitHub-Api-Version": "2022-11-28",
59
+ };
60
+ const call = async (path, what, init = {}) => {
61
+ const res = await fetchImpl(`${cfg.apiUrl}${path}`, { ...init, headers });
62
+ if (!res.ok) {
63
+ const text = await res.text().catch(() => "");
64
+ throw new Error(`GitHub: ${what} failed: HTTP ${res.status} ${text.slice(0, 300)}`);
65
+ }
66
+ return (await res.json());
67
+ };
68
+ const owner = cfg.repo.split("/")[0];
69
+ const open = await call(`/repos/${cfg.repo}/pulls?head=${encodeURIComponent(`${owner}:${pr.head}`)}` +
70
+ `&base=${encodeURIComponent(cfg.base)}&state=open`, `list the pull requests of ${pr.head}`);
71
+ const mine = open.find((p) => !p.base || p.base.ref === cfg.base);
72
+ if (mine) {
73
+ // What a person wrote stays: the title, once it names the ticket, and
74
+ // every line outside Specgate's own block of the description.
75
+ const patch = { body: mergeBody(mine.body ?? "", pr.body) };
76
+ const key = pr.key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
77
+ if (!new RegExp(`(^|[^A-Z0-9_-])${key}(?![0-9])`).test(mine.title ?? ""))
78
+ patch.title = pr.title;
79
+ const updated = await call(`/repos/${cfg.repo}/pulls/${mine.number}`, `update pull request #${mine.number}`, { method: "PATCH", body: JSON.stringify(patch) });
80
+ return { number: updated.number, url: updated.html_url, action: "updated" };
81
+ }
82
+ const created = await call(`/repos/${cfg.repo}/pulls`, `open a pull request for ${pr.head}`, {
83
+ method: "POST",
84
+ body: JSON.stringify({ title: pr.title, head: pr.head, base: cfg.base, body: pr.body }),
85
+ });
86
+ return { number: created.number, url: created.html_url, action: "created" };
87
+ }
88
+ //# sourceMappingURL=github-pr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-pr.js","sourceRoot":"","sources":["../../../scripts/vcs/github-pr.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsaglobaltech/specgate",
3
- "version": "0.21.0",
3
+ "version": "0.22.1",
4
4
  "description": "Specs as executable contracts — requirements, scenarios and traceability that CI enforces.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -22,12 +22,13 @@ The loop is propose → test → apply → archive:
22
22
  Each step in detail: the `/specgate:*` commands, or
23
23
  `specgate change instructions <artifact> --json`.
24
24
 
25
- ## From a plan or brief
25
+ ## From a brief or a Jira ticket
26
26
 
27
- `specgate propose --from-brief` names the next module of `docs/brief.md`;
28
- `specgate propose --from-brief <module>` opens it and writes `DRAFTING.md` —
29
- follow it, then re-run until "Ready to review". Never archive a draft
30
- yourself: a person reviews it, and it is archived after it is applied.
27
+ `specgate propose --from-brief <module>` or `specgate propose --from-ticket
28
+ <KEY>` opens the draft and writes `DRAFTING.md` — follow it, then re-run until
29
+ "Ready to review". Never archive a draft yourself: a person reviews it. Never
30
+ edit a change's `ticket.md`; start every commit message with the ticket key
31
+ (`KAN-13 REQ-007: …`), and never use Smart Commits (`#done`): they write to Jira.
31
32
 
32
33
  ## Never
33
34