@tablation/crew 0.78.1 → 0.79.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.
package/dist/cli.js
CHANGED
|
@@ -7131,6 +7131,28 @@ async function finishRunWorktree(dir, cfg, branch) {
|
|
|
7131
7131
|
// src/handoff-guard.ts
|
|
7132
7132
|
var HANDOFF_BOUNCED_MARKER = calloutHeader("WARNING", "Hand-off Bounced");
|
|
7133
7133
|
var pushedEventBody = (sha, remote, branch) => `pushed ${sha} to ${remote}/${branch}`;
|
|
7134
|
+
function acceptanceLines(description) {
|
|
7135
|
+
if (typeof description !== "string") return [];
|
|
7136
|
+
const out = [];
|
|
7137
|
+
let inside = false;
|
|
7138
|
+
for (const raw of description.split("\n")) {
|
|
7139
|
+
const heading = /^#{1,6}\s+(.*?)\s*$/.exec(raw);
|
|
7140
|
+
if (heading) {
|
|
7141
|
+
inside = /^acceptance\b/i.test(heading[1]);
|
|
7142
|
+
continue;
|
|
7143
|
+
}
|
|
7144
|
+
if (!inside) continue;
|
|
7145
|
+
const bullet = /^\s*(?:[-*+]|\d+[.)])\s+(?:\[[ xX]\]\s+)?(.+?)\s*$/.exec(raw);
|
|
7146
|
+
if (bullet) out.push(bullet[1]);
|
|
7147
|
+
}
|
|
7148
|
+
return out;
|
|
7149
|
+
}
|
|
7150
|
+
var flat = (s) => s.replace(/[`*_>]/g, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
7151
|
+
function unquotedAcceptance(lines, comment) {
|
|
7152
|
+
const body = flat(comment);
|
|
7153
|
+
return lines.filter((l) => !body.includes(flat(l)));
|
|
7154
|
+
}
|
|
7155
|
+
var ACCEPTANCE_BOUNCED_MARKER = calloutHeader("WARNING", "Acceptance Checklist Missing");
|
|
7134
7156
|
function remoteTip(dir, remote, branch) {
|
|
7135
7157
|
const out = gitOk(dir, ["ls-remote", "--heads", remote, `refs/heads/${branch}`]);
|
|
7136
7158
|
if (out === null) return null;
|
|
@@ -7206,6 +7228,27 @@ async function takenOverSince(i, ticketRow) {
|
|
|
7206
7228
|
return !!by && by !== i.shipName;
|
|
7207
7229
|
});
|
|
7208
7230
|
}
|
|
7231
|
+
async function missingAcceptance(i, t) {
|
|
7232
|
+
const lines = acceptanceLines(t.description);
|
|
7233
|
+
if (lines.length === 0 || !i.writer.comments) return null;
|
|
7234
|
+
const cc = i.contract.comments;
|
|
7235
|
+
const mine = (await i.writer.comments()).map((c) => c).filter((r) => r[cc.parent] === t.id && r[cc.author] === i.seatId).sort((a, b) => String(a[cc.createdAt] ?? "").localeCompare(String(b[cc.createdAt] ?? "")));
|
|
7236
|
+
const last = mine.at(-1);
|
|
7237
|
+
const missing = unquotedAcceptance(lines, last ? String(last[cc.body] ?? "") : "");
|
|
7238
|
+
return missing.length > 0 ? missing : null;
|
|
7239
|
+
}
|
|
7240
|
+
function acceptanceBounceComment(missing, shipName2) {
|
|
7241
|
+
return `${ACCEPTANCE_BOUNCED_MARKER}
|
|
7242
|
+
|
|
7243
|
+
**Hand-off bounced: the hand-off comment does not quote every line of the ticket's Acceptance section.**
|
|
7244
|
+
|
|
7245
|
+
${shipName2 ? `Ship: ${shipName2}
|
|
7246
|
+
|
|
7247
|
+
` : ""}Not quoted verbatim:
|
|
7248
|
+
${missing.map((l) => `- ${l}`).join("\n")}
|
|
7249
|
+
|
|
7250
|
+
Set back to \`in_progress\`. Post a comment that lists every Acceptance line verbatim, each followed by the test, command or manual check that exercised it in this run and its result (a line you could not exercise is listed with the reason, never omitted), then set \`fixed\` again. The branch is already pushed.`;
|
|
7251
|
+
}
|
|
7209
7252
|
async function enforceHandoff(i) {
|
|
7210
7253
|
try {
|
|
7211
7254
|
const t = await i.writer.ticket(i.ticketId);
|
|
@@ -7221,6 +7264,26 @@ async function enforceHandoff(i) {
|
|
|
7221
7264
|
i.log.warn(`${t.issue_id} is at ${t.status} but the push was not checked: ${v.why}`, log);
|
|
7222
7265
|
return { kind: "unverified", why: v.why };
|
|
7223
7266
|
}
|
|
7267
|
+
if (v.kind === "pushed" && !claimed) {
|
|
7268
|
+
const missing = await missingAcceptance(i, t);
|
|
7269
|
+
if (missing) {
|
|
7270
|
+
const heldNow = i.hold ? t[i.hold.column] : void 0;
|
|
7271
|
+
if (i.hold && heldNow && heldNow !== i.hold.shipId || await takenOverSince(i, t.id)) {
|
|
7272
|
+
i.log.warn(`${t.issue_id} is held by another ship now \u2014 the acceptance check is stale, left alone`, log);
|
|
7273
|
+
return { kind: "unverified", why: "ticket is held by another ship" };
|
|
7274
|
+
}
|
|
7275
|
+
await i.writer.updateTicket(t.id, {
|
|
7276
|
+
status: i.contract.statuses.building,
|
|
7277
|
+
...t.assignee_id ? {} : { assignee_id: i.seatId },
|
|
7278
|
+
...i.hold && !heldNow ? { [i.hold.column]: i.hold.shipId } : {}
|
|
7279
|
+
});
|
|
7280
|
+
await i.writer.postEvent(t.id, acceptanceBounceComment(missing, i.shipName), i.seatId, "bounced").catch(() => {
|
|
7281
|
+
i.log.warn("bounced, but the note failed to post", log);
|
|
7282
|
+
});
|
|
7283
|
+
i.log.warn(`${t.issue_id} bounced back to ${i.contract.statuses.building}: hand-off comment lacks the acceptance checklist`, log);
|
|
7284
|
+
return { kind: "bounced" };
|
|
7285
|
+
}
|
|
7286
|
+
}
|
|
7224
7287
|
if (v.kind === "pushed") {
|
|
7225
7288
|
await i.writer.postEvent(t.id, pushedEventBody(v.sha, v.remote, v.branch), i.seatId, "pushed");
|
|
7226
7289
|
i.log.emit(`${t.issue_id} pushed at ${v.sha.slice(0, 7)} \u2014 handed on to QA`, log);
|
package/package.json
CHANGED
|
@@ -94,6 +94,11 @@ pass.
|
|
|
94
94
|
Judge by exit code and summary line, and quote the summary in your
|
|
95
95
|
comment.
|
|
96
96
|
7. **Check the hand-off itself.**
|
|
97
|
+
- **Acceptance checklist.** Start from the builder's checklist: their
|
|
98
|
+
comment quotes each Acceptance line with the check that exercised it.
|
|
99
|
+
Re-run the checks that matter, and when a line fails, a bounce cites that
|
|
100
|
+
line by its quoted text. A line the builder marked unexercised is yours
|
|
101
|
+
to exercise.
|
|
97
102
|
- **Documentation.** If the repository's agent instructions (CLAUDE.md,
|
|
98
103
|
AGENTS.md, GEMINI.md or equivalent) name a user-facing documentation
|
|
99
104
|
location, a change to a user-facing surface must come with a matching
|
|
@@ -416,6 +416,14 @@ before going further, and never change a ticket's `report_type` yourself.
|
|
|
416
416
|
used, and anything you could not test yourself. Anything not committed and
|
|
417
417
|
pushed is gone.
|
|
418
418
|
|
|
419
|
+
**When the ticket has an Acceptance section, your last progress comment
|
|
420
|
+
quotes it.** List every line of that section verbatim, each followed by the
|
|
421
|
+
test, command or manual check that exercised it in this run and its result.
|
|
422
|
+
A line you could not exercise is listed with the reason, never omitted. The
|
|
423
|
+
runner checks your newest comment for every line and, if one is missing,
|
|
424
|
+
sets the ticket back to `in_progress` instead of passing it to QA. A ticket
|
|
425
|
+
with no Acceptance section is unaffected.
|
|
426
|
+
|
|
419
427
|
## Step 4 — you never merge, and you never deploy
|
|
420
428
|
|
|
421
429
|
**Nothing you do touches the base branch.** The release phase takes every
|
|
@@ -93,6 +93,11 @@ pass.
|
|
|
93
93
|
Judge by exit code and summary line, and quote the summary in your
|
|
94
94
|
comment.
|
|
95
95
|
7. **Check the hand-off itself.**
|
|
96
|
+
- **Acceptance checklist.** Start from the builder's checklist: their
|
|
97
|
+
comment quotes each Acceptance line with the check that exercised it.
|
|
98
|
+
Re-run the checks that matter, and when a line fails, a bounce cites that
|
|
99
|
+
line by its quoted text. A line the builder marked unexercised is yours
|
|
100
|
+
to exercise.
|
|
96
101
|
- **Documentation.** If the repository's agent instructions (CLAUDE.md,
|
|
97
102
|
AGENTS.md, GEMINI.md or equivalent) name a user-facing documentation
|
|
98
103
|
location, a change to a user-facing surface must come with a matching
|
|
@@ -411,6 +411,14 @@ before going further, and never change a ticket's `report_type` yourself.
|
|
|
411
411
|
used, and anything you could not test yourself. Anything not committed and
|
|
412
412
|
pushed is gone.
|
|
413
413
|
|
|
414
|
+
**When the ticket has an Acceptance section, your last progress comment
|
|
415
|
+
quotes it.** List every line of that section verbatim, each followed by the
|
|
416
|
+
test, command or manual check that exercised it in this run and its result.
|
|
417
|
+
A line you could not exercise is listed with the reason, never omitted. The
|
|
418
|
+
runner checks your newest comment for every line and, if one is missing,
|
|
419
|
+
sets the ticket back to `in_progress` instead of passing it to QA. A ticket
|
|
420
|
+
with no Acceptance section is unaffected.
|
|
421
|
+
|
|
414
422
|
## Step 4 — you never merge, and you never deploy
|
|
415
423
|
|
|
416
424
|
**Nothing you do touches the base branch.** The release phase takes every
|