@tablation/crew 0.41.0 → 0.42.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
|
@@ -3103,7 +3103,8 @@ var DEFAULT_CONTRACT = {
|
|
|
3103
3103
|
ship: "ship_id",
|
|
3104
3104
|
heldBy: "held_by_ship_id",
|
|
3105
3105
|
verifiedSha: "verified_sha",
|
|
3106
|
-
prRef: "pr_ref"
|
|
3106
|
+
prRef: "pr_ref",
|
|
3107
|
+
position: "position"
|
|
3107
3108
|
},
|
|
3108
3109
|
comments: {
|
|
3109
3110
|
parent: "ticket_id",
|
|
@@ -5391,20 +5392,25 @@ function issueNumber(t) {
|
|
|
5391
5392
|
function epicRank(t) {
|
|
5392
5393
|
return t.epicInProgress ? 0 : 1;
|
|
5393
5394
|
}
|
|
5395
|
+
function positionRank(t) {
|
|
5396
|
+
const p = t.position;
|
|
5397
|
+
return typeof p === "number" && Number.isFinite(p) ? p : Number.POSITIVE_INFINITY;
|
|
5398
|
+
}
|
|
5394
5399
|
function rank(t, c = DEFAULT_CONTRACT) {
|
|
5395
|
-
return [effectivePriority(t, c), severityRank(t, c), epicRank(t), issueNumber(t)];
|
|
5400
|
+
return [effectivePriority(t, c), positionRank(t), severityRank(t, c), epicRank(t), issueNumber(t)];
|
|
5396
5401
|
}
|
|
5397
5402
|
function compareRank(a, b, c = DEFAULT_CONTRACT) {
|
|
5398
5403
|
const ra = rank(a, c);
|
|
5399
5404
|
const rb = rank(b, c);
|
|
5400
5405
|
for (let i = 0; i < ra.length; i++) {
|
|
5401
|
-
const
|
|
5402
|
-
|
|
5406
|
+
const x = ra[i];
|
|
5407
|
+
const y = rb[i];
|
|
5408
|
+
if (x !== y) return x < y ? -1 : 1;
|
|
5403
5409
|
}
|
|
5404
5410
|
return 0;
|
|
5405
5411
|
}
|
|
5406
5412
|
function rankScalar(t, c = DEFAULT_CONTRACT) {
|
|
5407
|
-
const [p, s, e, n] = rank(t, c);
|
|
5413
|
+
const [p, , s, e, n] = rank(t, c);
|
|
5408
5414
|
return p * 1e9 + s * 1e7 + e * 1e6 + n;
|
|
5409
5415
|
}
|
|
5410
5416
|
var NOTHING_ACTIONABLE = 999999999999;
|
|
@@ -5611,7 +5617,7 @@ function claimStatuses(role, c) {
|
|
|
5611
5617
|
}
|
|
5612
5618
|
function rankedCandidates(role, i) {
|
|
5613
5619
|
const c = i.contract ?? DEFAULT_CONTRACT;
|
|
5614
|
-
return [...roleCandidates(role, i)].sort((a, b) =>
|
|
5620
|
+
return [...roleCandidates(role, i)].sort((a, b) => compareRank(a, b, c));
|
|
5615
5621
|
}
|
|
5616
5622
|
function roleTopRank(role, i) {
|
|
5617
5623
|
if (role !== "qa" && role !== "triage" && !i.seats[role]) return NOTHING_ACTIONABLE;
|
|
@@ -5624,7 +5630,7 @@ function actionableSummary(i, pending) {
|
|
|
5624
5630
|
const c = i.contract ?? DEFAULT_CONTRACT;
|
|
5625
5631
|
const all = pending.flatMap((role) => roleCandidates(role, i));
|
|
5626
5632
|
if (all.length === 0) return { count: 0 };
|
|
5627
|
-
const top = all.reduce((best, t) =>
|
|
5633
|
+
const top = all.reduce((best, t) => compareRank(t, best, c) < 0 ? t : best);
|
|
5628
5634
|
return { count: all.length, top };
|
|
5629
5635
|
}
|
|
5630
5636
|
var FIXED_RANK = { qa: -2, triage: -1 };
|
|
@@ -5850,6 +5856,7 @@ function blockers(t, i) {
|
|
|
5850
5856
|
function modeCell(t, i) {
|
|
5851
5857
|
return i.workModes && i.workModes.modes.size > 0 ? modeOf(t, i.workModes) : "\u2014";
|
|
5852
5858
|
}
|
|
5859
|
+
var effCell = (t) => `p${effectivePriority(t)}${positionRank(t) === Number.POSITIVE_INFINITY ? "" : ` #${positionRank(t)}`}`;
|
|
5853
5860
|
var sorted = (ts) => [...ts].sort(compareRank);
|
|
5854
5861
|
var stream = (segments) => `${segments.join("\n")}
|
|
5855
5862
|
`;
|
|
@@ -5868,7 +5875,7 @@ function buildingDigest(i) {
|
|
|
5868
5875
|
const worktree = i.worktreeFor ? i.worktreeFor(t) : "";
|
|
5869
5876
|
const dir = i.dirFor ? i.dirFor(t) : null;
|
|
5870
5877
|
const repo = i.dirFor ? dir ?? "**NO CHECKOUT**" : "";
|
|
5871
|
-
return `| ${displayKey2(t)} | ${repo} | ${branch} | ${worktree} | ${modeCell(t, i)} | ${t.status} | ${who(t, i)} | ${filedBy(t, i)} | ${t.severity ?? "\u2014"} | ${t.priority ?? "\u2014"} |
|
|
5878
|
+
return `| ${displayKey2(t)} | ${repo} | ${branch} | ${worktree} | ${modeCell(t, i)} | ${t.status} | ${who(t, i)} | ${filedBy(t, i)} | ${t.severity ?? "\u2014"} | ${t.priority ?? "\u2014"} | ${effCell(t)} | ${t.updated_at.slice(0, 16)}Z | ${lastComment(i, t.id)} | ${n > 0 ? `**${n} new**` : "\u2014"} |`;
|
|
5872
5879
|
};
|
|
5873
5880
|
const blockedRow = (t) => `| ${displayKey2(t)} | ${t.status} | ${who(t, i)} | p${effectivePriority(t)} | ${blockers(t, i)} |`;
|
|
5874
5881
|
const blockedTable = (ts) => ts.length === 0 ? "\n_None._\n" : `
|
|
@@ -5929,7 +5936,7 @@ function qaDigest(i) {
|
|
|
5929
5936
|
const n = newFromOthers(i, t.id);
|
|
5930
5937
|
const dir = i.dirFor ? i.dirFor(t) : null;
|
|
5931
5938
|
const repo = i.dirFor ? dir ?? "**NO CHECKOUT**" : "";
|
|
5932
|
-
return `| ${displayKey2(t)} | ${repo} | ${modeCell(t, i)} | ${t.status} | ${builtBy(t)} | ${who(t, i)} | ${filedBy(t, i)} | ${t.severity ?? "\u2014"} | ${t.priority ?? "\u2014"} |
|
|
5939
|
+
return `| ${displayKey2(t)} | ${repo} | ${modeCell(t, i)} | ${t.status} | ${builtBy(t)} | ${who(t, i)} | ${filedBy(t, i)} | ${t.severity ?? "\u2014"} | ${t.priority ?? "\u2014"} | ${effCell(t)} | ${branchCell(t)} | ${worktreeCell(t)} | ${t.updated_at.slice(0, 16)}Z | ${lastComment(i, t.id)} | ${n > 0 ? `**${n} new**` : "\u2014"} |`;
|
|
5933
5940
|
};
|
|
5934
5941
|
const waiting = i.tickets.filter((t) => (t.status === "qa" || t.status === "fixed") && i.blocked.has(t.id));
|
|
5935
5942
|
const takeable = (status2) => i.tickets.filter((t) => t.status === status2 && !i.blocked.has(t.id));
|
|
@@ -6124,6 +6131,7 @@ async function decideCycle(o) {
|
|
|
6124
6131
|
data: { tickets: tickets.length, comments: comments.length }
|
|
6125
6132
|
});
|
|
6126
6133
|
const epicColumn = tracker.contract.columns.epic;
|
|
6134
|
+
const positionColumn = tracker.contract.columns.position;
|
|
6127
6135
|
const epicBuilding = tracker.contract.epics.statuses.building;
|
|
6128
6136
|
const epicStatusColumn = tracker.contract.epics.statusColumn;
|
|
6129
6137
|
const epicStatusById = new Map(epicRows.map((e) => [e.id, e[epicStatusColumn] ?? null]));
|
|
@@ -6143,6 +6151,9 @@ async function decideCycle(o) {
|
|
|
6143
6151
|
for (const t of tickets) {
|
|
6144
6152
|
const epicId = t[epicColumn];
|
|
6145
6153
|
t.epicInProgress = typeof epicId === "string" && epicStatusById.get(epicId) === epicBuilding;
|
|
6154
|
+
const pos = t[positionColumn];
|
|
6155
|
+
const n = typeof pos === "string" && pos.trim() !== "" ? Number(pos) : pos;
|
|
6156
|
+
t.position = typeof n === "number" && Number.isFinite(n) ? n : null;
|
|
6146
6157
|
}
|
|
6147
6158
|
const extra = await tracker.ticketsByIds(missingBlockerIds(tickets));
|
|
6148
6159
|
const info = blockerInfoMap(tickets, extra);
|
package/package.json
CHANGED
|
@@ -183,13 +183,17 @@ should is Step 1's call, not a filesystem check.
|
|
|
183
183
|
and then it is a stop for a person (see "Stopping"), never a bare comment.
|
|
184
184
|
- Otherwise pick from the digest's "Step 2" table, which is already
|
|
185
185
|
`accepted`, in your lane and in order. Absent a digest, fetch those tickets
|
|
186
|
-
and order them by **effective priority, then severity, then `issue_id`**:
|
|
186
|
+
and order them by **effective priority, then manual position, then severity, then `issue_id`**:
|
|
187
187
|
- **Effective priority** is the stronger of the ticket's own `priority`
|
|
188
188
|
and one derived from `severity` (`s1`→p0, `s2`→p1, `s3`→p2, `s4`→p3;
|
|
189
189
|
neither set → p2). p0 beats p1 beats p2 beats p3, so an explicit
|
|
190
190
|
priority can only move a ticket *forward*. Priority is an escalation
|
|
191
191
|
lever; to rank something down, lower its severity.
|
|
192
|
-
- Within one tier,
|
|
192
|
+
- Within one tier, a ticket with a manual **`position`** (a lower number
|
|
193
|
+
is earlier) goes before one without; position only orders tickets of the
|
|
194
|
+
same effective priority and never lets a lower tier jump a higher one. A
|
|
195
|
+
workspace with no position column skips this clause.
|
|
196
|
+
- Then more severe first (`s1` … `s4`, then unset).
|
|
193
197
|
- Within one severity, a ticket whose **epic is already in progress** goes
|
|
194
198
|
first (finishing beats starting); a `planned` epic or none ranks the
|
|
195
199
|
same. Only then oldest `issue_id` first.
|
|
@@ -183,13 +183,17 @@ should is Step 1's call, not a filesystem check.
|
|
|
183
183
|
and then it is a stop for a person (see "Stopping"), never a bare comment.
|
|
184
184
|
- Otherwise pick from the digest's "Step 2" table, which is already
|
|
185
185
|
`accepted`, in your lane and in order. Absent a digest, fetch those tickets
|
|
186
|
-
and order them by **effective priority, then severity, then `issue_id`**:
|
|
186
|
+
and order them by **effective priority, then manual position, then severity, then `issue_id`**:
|
|
187
187
|
- **Effective priority** is the stronger of the ticket's own `priority`
|
|
188
188
|
and one derived from `severity` (`s1`→p0, `s2`→p1, `s3`→p2, `s4`→p3;
|
|
189
189
|
neither set → p2). p0 beats p1 beats p2 beats p3, so an explicit
|
|
190
190
|
priority can only move a ticket *forward*. Priority is an escalation
|
|
191
191
|
lever; to rank something down, lower its severity.
|
|
192
|
-
- Within one tier,
|
|
192
|
+
- Within one tier, a ticket with a manual **`position`** (a lower number
|
|
193
|
+
is earlier) goes before one without; position only orders tickets of the
|
|
194
|
+
same effective priority and never lets a lower tier jump a higher one. A
|
|
195
|
+
workspace with no position column skips this clause.
|
|
196
|
+
- Then more severe first (`s1` … `s4`, then unset).
|
|
193
197
|
- Within one severity, a ticket whose **epic is already in progress** goes
|
|
194
198
|
first (finishing beats starting); a `planned` epic or none ranks the
|
|
195
199
|
same. Only then oldest `issue_id` first.
|