@tablation/crew 0.75.1 → 0.75.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/dist/cli.js +63 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5168,6 +5168,7 @@ var DEFAULT_CONTRACT = {
|
|
|
5168
5168
|
resolutionNote: "resolution_note",
|
|
5169
5169
|
verifiedSha: "verified_sha",
|
|
5170
5170
|
prRef: "pr_ref",
|
|
5171
|
+
externalId: "external_id",
|
|
5171
5172
|
position: "position"
|
|
5172
5173
|
},
|
|
5173
5174
|
comments: {
|
|
@@ -5652,6 +5653,29 @@ var State = class _State {
|
|
|
5652
5653
|
}
|
|
5653
5654
|
};
|
|
5654
5655
|
}
|
|
5656
|
+
/**
|
|
5657
|
+
* The previous cycle's unroutable ticket ids for one route (CREW-2548): open
|
|
5658
|
+
* tickets with no repository that are not exempt. `poll.ts` diffs against it
|
|
5659
|
+
* so each ticket warns once when it becomes unroutable, not every cycle; the
|
|
5660
|
+
* caller persists the new set beside `attention(route).persist(...)`. A
|
|
5661
|
+
* missing or corrupt file reads as empty, so a fresh install warns once.
|
|
5662
|
+
*/
|
|
5663
|
+
unroutable(route) {
|
|
5664
|
+
const file = `.unroutable-${_State.safe(route)}.json`;
|
|
5665
|
+
return {
|
|
5666
|
+
previous: () => {
|
|
5667
|
+
try {
|
|
5668
|
+
const parsed = JSON.parse(readFileSync12(this.path(file), "utf8"));
|
|
5669
|
+
return Array.isArray(parsed) ? parsed.filter((x) => typeof x === "string") : [];
|
|
5670
|
+
} catch {
|
|
5671
|
+
return [];
|
|
5672
|
+
}
|
|
5673
|
+
},
|
|
5674
|
+
persist: (next) => {
|
|
5675
|
+
writeFileSync8(this.path(file), JSON.stringify(next));
|
|
5676
|
+
}
|
|
5677
|
+
};
|
|
5678
|
+
}
|
|
5655
5679
|
/**
|
|
5656
5680
|
* Open ship-level attention items (CREW-1373), ship-wide rather than
|
|
5657
5681
|
* per-route: a parked role or a missing hook command is about this
|
|
@@ -9349,6 +9373,14 @@ function filingErrors(tickets, opts = {}) {
|
|
|
9349
9373
|
return !exempt.has(t[reportTypeColumn]);
|
|
9350
9374
|
});
|
|
9351
9375
|
}
|
|
9376
|
+
function unroutableChanges(unroutable, previous, tickets, externalIdColumn) {
|
|
9377
|
+
const prev = new Set(previous);
|
|
9378
|
+
const now = new Set(unroutable.map((t) => t.id));
|
|
9379
|
+
return {
|
|
9380
|
+
added: unroutable.filter((t) => !prev.has(t.id)).map((t) => ({ ticket: t, synced: !!t[externalIdColumn] })),
|
|
9381
|
+
gained: tickets.filter((t) => prev.has(t.id) && !now.has(t.id) && !!t.repo_id)
|
|
9382
|
+
};
|
|
9383
|
+
}
|
|
9352
9384
|
function sweepComment(step) {
|
|
9353
9385
|
return step.action === "park" ? `Parked as **Blocked** by the crew \u2014 waiting on: ${step.blockers}.
|
|
9354
9386
|
|
|
@@ -10131,15 +10163,30 @@ async function decideCycle(o) {
|
|
|
10131
10163
|
emit.emit(`every child is done \u2014 this coordinating ticket is too`, { ticket: r.parent.issue_id });
|
|
10132
10164
|
}
|
|
10133
10165
|
}
|
|
10134
|
-
|
|
10166
|
+
const unroutableTickets = filingErrors(tickets, {
|
|
10135
10167
|
...coordinating,
|
|
10136
10168
|
repoExemptValues: tracker.contract.repoExemptReportTypes
|
|
10137
|
-
})
|
|
10169
|
+
});
|
|
10170
|
+
const unroutable = unroutableTickets.map((t) => t.id);
|
|
10171
|
+
const { added, gained } = unroutableChanges(
|
|
10172
|
+
unroutableTickets,
|
|
10173
|
+
state.unroutable(route.route).previous(),
|
|
10174
|
+
tickets,
|
|
10175
|
+
tracker.contract.columns.externalId
|
|
10176
|
+
);
|
|
10177
|
+
for (const { ticket: t, synced } of added) {
|
|
10138
10178
|
emit.warn(
|
|
10139
|
-
"no repo and not marked coordinating/question/investigation \u2014 a filing error, not an epic, and unroutable as written",
|
|
10179
|
+
synced ? "no repository \u2014 not crew work until one is set" : "no repo and not marked coordinating/question/investigation \u2014 a filing error, not an epic, and unroutable as written",
|
|
10140
10180
|
{ ticket: t.issue_id }
|
|
10141
10181
|
);
|
|
10142
10182
|
}
|
|
10183
|
+
for (const t of gained) emit.emit("now has a repository \u2014 no longer unroutable", { ticket: t.issue_id });
|
|
10184
|
+
if (unroutableTickets.length) {
|
|
10185
|
+
emit.emit(
|
|
10186
|
+
`${unroutableTickets.length} open ticket(s) have no repository and are not routable (${added.length} new since last cycle)`,
|
|
10187
|
+
{ step: "sweep", data: { unroutable: unroutableTickets.map((t) => t.issue_id), newUnroutable: added.map((a) => a.ticket.issue_id) } }
|
|
10188
|
+
);
|
|
10189
|
+
}
|
|
10143
10190
|
const attentionCurrent = {};
|
|
10144
10191
|
for (const t of tickets) {
|
|
10145
10192
|
const reasons = attentionReasons(t, tracker.contract);
|
|
@@ -10212,7 +10259,8 @@ async function decideCycle(o) {
|
|
|
10212
10259
|
watermark,
|
|
10213
10260
|
directionEvents,
|
|
10214
10261
|
selectionInput,
|
|
10215
|
-
attention: attentionCurrent
|
|
10262
|
+
attention: attentionCurrent,
|
|
10263
|
+
unroutable
|
|
10216
10264
|
};
|
|
10217
10265
|
const advanced = state.advanceWatermark([...comments, ...tickets]);
|
|
10218
10266
|
if (advanced !== watermark) {
|
|
@@ -13128,7 +13176,10 @@ async function decideFleet(o) {
|
|
|
13128
13176
|
if (e.error || !e.decision) continue;
|
|
13129
13177
|
const won = best?.route.route === e.route.route;
|
|
13130
13178
|
o.state.fairness(e.route.route).record(e.decision.actionable.count, e.decision.actionable.top?.issue_id, won);
|
|
13131
|
-
if (!o.dryRun)
|
|
13179
|
+
if (!o.dryRun) {
|
|
13180
|
+
o.state.attention(e.route.route).persist(e.decision.attention);
|
|
13181
|
+
o.state.unroutable(e.route.route).persist(e.decision.unroutable);
|
|
13182
|
+
}
|
|
13132
13183
|
}
|
|
13133
13184
|
return {
|
|
13134
13185
|
entries,
|
|
@@ -16276,6 +16327,7 @@ async function runOnePassUnguarded(o) {
|
|
|
16276
16327
|
}
|
|
16277
16328
|
const decision = await decide(ctx);
|
|
16278
16329
|
o.state.attention(o.route.route).persist(decision.attention);
|
|
16330
|
+
o.state.unroutable(o.route.route).persist(decision.unroutable);
|
|
16279
16331
|
if (decision.sweep.length) {
|
|
16280
16332
|
const tracker = new Tracker(o.route, o.ship);
|
|
16281
16333
|
const seat = o.route.resolved?.seats.qa ?? o.route.resolved?.seats.dev ?? "";
|
|
@@ -18034,7 +18086,10 @@ ${changed2.map((c) => ` - ${c}
|
|
|
18034
18086
|
break;
|
|
18035
18087
|
}
|
|
18036
18088
|
const decision = await decideCycle({ route, ship: cfg.ship, state, emit });
|
|
18037
|
-
if (!dryRun)
|
|
18089
|
+
if (!dryRun) {
|
|
18090
|
+
state.attention(route.route).persist(decision.attention);
|
|
18091
|
+
state.unroutable(route.route).persist(decision.unroutable);
|
|
18092
|
+
}
|
|
18038
18093
|
if (decision.sweep.length) {
|
|
18039
18094
|
if (dryRun) {
|
|
18040
18095
|
for (const s of decision.sweep) {
|
|
@@ -20567,6 +20622,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
20567
20622
|
// Additive (CREW-1496): each project and its work mode. `version` stays 1.
|
|
20568
20623
|
projects: projects.map((p) => ({ id: p.id, name: p.name, workMode: p.mode })),
|
|
20569
20624
|
dir: route.dir,
|
|
20625
|
+
// Additive (CREW-2548): open tickets with no repository, as of the last cycle.
|
|
20626
|
+
unroutable: state.unroutable(route.route).previous().length,
|
|
20570
20627
|
enabled: route.enabled,
|
|
20571
20628
|
area: route.area ?? null,
|
|
20572
20629
|
// Additive (CREW-1511): the toolchain this route's sessions run on.
|
package/package.json
CHANGED