@tablation/crew 0.61.0 → 0.62.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 +249 -149
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5859,154 +5859,8 @@ function resetIncomplete(steps) {
|
|
|
5859
5859
|
return steps.some((s) => s.action === "failed" || s.action === "kept");
|
|
5860
5860
|
}
|
|
5861
5861
|
|
|
5862
|
-
// src/
|
|
5863
|
-
import {
|
|
5864
|
-
import { TablationClient as TablationClient3 } from "@tablation/client";
|
|
5865
|
-
|
|
5866
|
-
// src/contract.ts
|
|
5867
|
-
var DEFAULT_CONTRACT = {
|
|
5868
|
-
columns: {
|
|
5869
|
-
key: "issue_id",
|
|
5870
|
-
tag: "issue_tag",
|
|
5871
|
-
title: "title",
|
|
5872
|
-
status: "status",
|
|
5873
|
-
assignee: "assignee_id",
|
|
5874
|
-
priority: "priority",
|
|
5875
|
-
severity: "severity",
|
|
5876
|
-
slice: "project_id",
|
|
5877
|
-
repo: "repo_id",
|
|
5878
|
-
parent: "parent_id",
|
|
5879
|
-
blockedBy: "blocked_by",
|
|
5880
|
-
updatedAt: "updated_at",
|
|
5881
|
-
needsDesign: "needs_design",
|
|
5882
|
-
attachments: "attachments",
|
|
5883
|
-
workMode: "work_mode",
|
|
5884
|
-
reportType: "report_type",
|
|
5885
|
-
epic: "epic_id",
|
|
5886
|
-
author: "filed_by_id",
|
|
5887
|
-
ship: "ship_id",
|
|
5888
|
-
heldBy: "held_by_ship_id",
|
|
5889
|
-
resolutionNote: "resolution_note",
|
|
5890
|
-
verifiedSha: "verified_sha",
|
|
5891
|
-
prRef: "pr_ref",
|
|
5892
|
-
position: "position"
|
|
5893
|
-
},
|
|
5894
|
-
comments: {
|
|
5895
|
-
parent: "ticket_id",
|
|
5896
|
-
body: "body",
|
|
5897
|
-
author: "team_member_id",
|
|
5898
|
-
createdAt: "created_at",
|
|
5899
|
-
ship: "ship_id",
|
|
5900
|
-
publicReply: "is_public_reply",
|
|
5901
|
-
authorName: "author_name"
|
|
5902
|
-
},
|
|
5903
|
-
statuses: {
|
|
5904
|
-
open: ["new", "accepted", "blocked", "in_progress", "needs_info", "fixed", "qa", "verified"],
|
|
5905
|
-
resolved: [
|
|
5906
|
-
"verified",
|
|
5907
|
-
"closed_deployed",
|
|
5908
|
-
"closed_wont_fix",
|
|
5909
|
-
"closed_duplicate",
|
|
5910
|
-
"closed_completed",
|
|
5911
|
-
"closed_cant_reproduce"
|
|
5912
|
-
],
|
|
5913
|
-
approved: "accepted",
|
|
5914
|
-
parked: "blocked",
|
|
5915
|
-
building: "in_progress",
|
|
5916
|
-
handoff: "fixed",
|
|
5917
|
-
verifying: "qa",
|
|
5918
|
-
verified: "verified",
|
|
5919
|
-
deployed: "closed_deployed",
|
|
5920
|
-
needsHuman: "needs_info",
|
|
5921
|
-
reviewing: null,
|
|
5922
|
-
wontFix: "closed_wont_fix",
|
|
5923
|
-
draft: "draft"
|
|
5924
|
-
},
|
|
5925
|
-
epics: {
|
|
5926
|
-
statusColumn: "status",
|
|
5927
|
-
keyColumn: "epic_id",
|
|
5928
|
-
statuses: { planned: "planned", building: "in_progress", done: "done", cancelled: "cancelled" }
|
|
5929
|
-
},
|
|
5930
|
-
priorityOrder: ["p0", "p1", "p2", "p3"],
|
|
5931
|
-
severityOrder: ["s1", "s2", "s3", "s4"],
|
|
5932
|
-
unknownPriorityRank: 2,
|
|
5933
|
-
coordinatingValue: "coordinating",
|
|
5934
|
-
manualWorkMode: "manual",
|
|
5935
|
-
hybridWorkMode: "hybrid",
|
|
5936
|
-
repoExemptReportTypes: ["coordinating", "question", "investigation"]
|
|
5937
|
-
};
|
|
5938
|
-
function closedStatuses(c) {
|
|
5939
|
-
return c.statuses.resolved.filter((s) => s !== c.statuses.verified);
|
|
5940
|
-
}
|
|
5941
|
-
function resolveContract(override) {
|
|
5942
|
-
if (!override) return DEFAULT_CONTRACT;
|
|
5943
|
-
return {
|
|
5944
|
-
columns: { ...DEFAULT_CONTRACT.columns, ...override.columns },
|
|
5945
|
-
comments: { ...DEFAULT_CONTRACT.comments, ...override.comments },
|
|
5946
|
-
statuses: { ...DEFAULT_CONTRACT.statuses, ...override.statuses },
|
|
5947
|
-
epics: {
|
|
5948
|
-
...DEFAULT_CONTRACT.epics,
|
|
5949
|
-
...override.epics,
|
|
5950
|
-
statuses: { ...DEFAULT_CONTRACT.epics.statuses, ...override.epics?.statuses }
|
|
5951
|
-
},
|
|
5952
|
-
priorityOrder: override.priorityOrder ?? DEFAULT_CONTRACT.priorityOrder,
|
|
5953
|
-
severityOrder: override.severityOrder ?? DEFAULT_CONTRACT.severityOrder,
|
|
5954
|
-
unknownPriorityRank: override.unknownPriorityRank ?? DEFAULT_CONTRACT.unknownPriorityRank,
|
|
5955
|
-
coordinatingValue: override.coordinatingValue ?? DEFAULT_CONTRACT.coordinatingValue,
|
|
5956
|
-
manualWorkMode: override.manualWorkMode ?? DEFAULT_CONTRACT.manualWorkMode,
|
|
5957
|
-
hybridWorkMode: override.hybridWorkMode ?? DEFAULT_CONTRACT.hybridWorkMode,
|
|
5958
|
-
// A workspace overriding `coordinatingValue` alone (renaming the rollup
|
|
5959
|
-
// marker) still gets that renamed value exempted here by default,
|
|
5960
|
-
// without also having to repeat it — only an explicit
|
|
5961
|
-
// `repoExemptReportTypes` overrides this derived default.
|
|
5962
|
-
repoExemptReportTypes: override.repoExemptReportTypes ?? (override.coordinatingValue ? [override.coordinatingValue, "question", "investigation"] : DEFAULT_CONTRACT.repoExemptReportTypes)
|
|
5963
|
-
};
|
|
5964
|
-
}
|
|
5965
|
-
function validateContract(c) {
|
|
5966
|
-
const problems = [];
|
|
5967
|
-
const open = new Set(c.statuses.open);
|
|
5968
|
-
const resolved = new Set(c.statuses.resolved);
|
|
5969
|
-
for (const role of ["approved", "parked", "building", "handoff", "verifying", "needsHuman"]) {
|
|
5970
|
-
const v = c.statuses[role];
|
|
5971
|
-
if (!open.has(v)) problems.push(`statuses.${role} ("${v}") is not listed in statuses.open`);
|
|
5972
|
-
}
|
|
5973
|
-
if (c.statuses.reviewing && !open.has(c.statuses.reviewing)) {
|
|
5974
|
-
problems.push(`statuses.reviewing ("${c.statuses.reviewing}") is not listed in statuses.open`);
|
|
5975
|
-
}
|
|
5976
|
-
if (!resolved.has(c.statuses.deployed)) {
|
|
5977
|
-
problems.push(`statuses.deployed ("${c.statuses.deployed}") is not listed in statuses.resolved`);
|
|
5978
|
-
}
|
|
5979
|
-
if (resolved.has(c.statuses.handoff)) {
|
|
5980
|
-
problems.push(
|
|
5981
|
-
`statuses.handoff ("${c.statuses.handoff}") is listed as resolved \u2014 it means built-but-unchecked, so a ticket blocked by it is still blocked; calling it resolved would build on unverified work`
|
|
5982
|
-
);
|
|
5983
|
-
}
|
|
5984
|
-
if (!resolved.has(c.statuses.wontFix)) {
|
|
5985
|
-
problems.push(`statuses.wontFix ("${c.statuses.wontFix}") is not listed in statuses.resolved`);
|
|
5986
|
-
}
|
|
5987
|
-
if (c.statuses.reviewing && resolved.has(c.statuses.reviewing)) {
|
|
5988
|
-
problems.push(
|
|
5989
|
-
`statuses.reviewing ("${c.statuses.reviewing}") is listed as resolved \u2014 it is unmerged work under review, so a ticket blocked by it is still blocked`
|
|
5990
|
-
);
|
|
5991
|
-
}
|
|
5992
|
-
if (resolved.has(c.statuses.approved)) {
|
|
5993
|
-
problems.push(`statuses.approved ("${c.statuses.approved}") is listed as resolved`);
|
|
5994
|
-
}
|
|
5995
|
-
if (c.statuses.parked === c.statuses.approved) {
|
|
5996
|
-
problems.push("statuses.parked and statuses.approved are the same value \u2014 parking would be a no-op");
|
|
5997
|
-
}
|
|
5998
|
-
const epicValues = Object.values(c.epics.statuses);
|
|
5999
|
-
if (new Set(epicValues).size !== epicValues.length) {
|
|
6000
|
-
problems.push("epics.statuses names the same value twice \u2014 the epic sync could not tell those states apart");
|
|
6001
|
-
}
|
|
6002
|
-
if (c.priorityOrder.length === 0) problems.push("priorityOrder is empty");
|
|
6003
|
-
if (c.severityOrder.length === 0) problems.push("severityOrder is empty");
|
|
6004
|
-
for (const [name, order] of [["priorityOrder", c.priorityOrder], ["severityOrder", c.severityOrder]]) {
|
|
6005
|
-
if (new Set(order).size !== order.length) problems.push(`${name} contains duplicates`);
|
|
6006
|
-
}
|
|
6007
|
-
return problems;
|
|
6008
|
-
}
|
|
6009
|
-
var EVENT_LOG_MISSING = "the Issues table has no event log \u2014 crew writes its audit trail there (CREW-1574). In the app, open the Issues table's Data & lifecycle tab and turn on Event log, then rerun.";
|
|
5862
|
+
// src/crew-floor.ts
|
|
5863
|
+
import { TablationApiError as TablationApiError3 } from "@tablation/client";
|
|
6010
5864
|
|
|
6011
5865
|
// src/callout.ts
|
|
6012
5866
|
var calloutHeader = (kind, label) => `> [!${kind}]
|
|
@@ -7055,6 +6909,223 @@ var SHELL_WORDS = /* @__PURE__ */ new Set([
|
|
|
7055
6909
|
"ok"
|
|
7056
6910
|
]);
|
|
7057
6911
|
|
|
6912
|
+
// src/crew-floor.ts
|
|
6913
|
+
var CREW_OUTDATED_KEY = "crew_outdated";
|
|
6914
|
+
function parseVersion(v) {
|
|
6915
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(v.trim());
|
|
6916
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
6917
|
+
}
|
|
6918
|
+
function compareVersions(a, b) {
|
|
6919
|
+
const pa = parseVersion(a);
|
|
6920
|
+
const pb = parseVersion(b);
|
|
6921
|
+
if (!pa || !pb) return null;
|
|
6922
|
+
for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return pa[i] - pb[i];
|
|
6923
|
+
return 0;
|
|
6924
|
+
}
|
|
6925
|
+
function belowFloor(have, floor) {
|
|
6926
|
+
if (!floor) return false;
|
|
6927
|
+
const c = compareVersions(have, floor);
|
|
6928
|
+
return c !== null && c < 0;
|
|
6929
|
+
}
|
|
6930
|
+
function outdatedItem(have, floor, nowIso = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
6931
|
+
return {
|
|
6932
|
+
kind: "crew_outdated",
|
|
6933
|
+
key: CREW_OUTDATED_KEY,
|
|
6934
|
+
since: nowIso,
|
|
6935
|
+
message: `crew ${have} is below this workspace's minimum ${floor} \u2014 no ticket will be picked until it is updated (the Crew app updates it, or \`npm install -g @tablation/crew\`). Resumes automatically once the version satisfies the floor`
|
|
6936
|
+
};
|
|
6937
|
+
}
|
|
6938
|
+
function sentVersion(ship) {
|
|
6939
|
+
return /CrewAgent\/(\d+\.\d+\.\d+\S*)/.exec(ship.userAgent)?.[1] ?? null;
|
|
6940
|
+
}
|
|
6941
|
+
async function readCrewFloor(route, ship) {
|
|
6942
|
+
const r = route.resolved;
|
|
6943
|
+
if (!r?.projectId) return void 0;
|
|
6944
|
+
try {
|
|
6945
|
+
const res = await fetch(`${route.baseUrl}/api/projects/${encodeURIComponent(r.projectId)}?workspaceId=${r.workspaceId}`, {
|
|
6946
|
+
headers: { Authorization: `Bearer ${resolveApiKey(route)}`, "User-Agent": ship.userAgent }
|
|
6947
|
+
});
|
|
6948
|
+
if (!res.ok) return void 0;
|
|
6949
|
+
const body = await res.json();
|
|
6950
|
+
return body.crewMinVersion ?? null;
|
|
6951
|
+
} catch {
|
|
6952
|
+
return void 0;
|
|
6953
|
+
}
|
|
6954
|
+
}
|
|
6955
|
+
async function checkCrewFloor(o, deps = {}) {
|
|
6956
|
+
const have = deps.version ?? sentVersion(o.ship);
|
|
6957
|
+
if (!have) return null;
|
|
6958
|
+
const floor = await (deps.read ?? readCrewFloor)(o.route, o.ship);
|
|
6959
|
+
if (floor === void 0) return openOutdated(o.state);
|
|
6960
|
+
if (belowFloor(have, floor)) {
|
|
6961
|
+
const item = outdatedItem(have, floor);
|
|
6962
|
+
await raiseShipAttention(o, item);
|
|
6963
|
+
return item.message;
|
|
6964
|
+
}
|
|
6965
|
+
clearShipAttention(o.state, CREW_OUTDATED_KEY);
|
|
6966
|
+
return null;
|
|
6967
|
+
}
|
|
6968
|
+
function openOutdated(state) {
|
|
6969
|
+
return state.shipAttention().previous().find((i) => i.key === CREW_OUTDATED_KEY)?.message ?? null;
|
|
6970
|
+
}
|
|
6971
|
+
function isUpgradeRequired(err) {
|
|
6972
|
+
return err instanceof TablationApiError3 && err.status === 426;
|
|
6973
|
+
}
|
|
6974
|
+
async function raiseFromUpgradeRequired(o, err) {
|
|
6975
|
+
const have = sentVersion(o.ship) ?? "unknown";
|
|
6976
|
+
const floor = /(\d+\.\d+\.\d+)/.exec(err.message.replace(have, ""))?.[1] ?? "a newer version";
|
|
6977
|
+
await raiseShipAttention(o, outdatedItem(have, floor));
|
|
6978
|
+
}
|
|
6979
|
+
|
|
6980
|
+
// src/tracker.ts
|
|
6981
|
+
import { hostname } from "os";
|
|
6982
|
+
import { TablationClient as TablationClient3 } from "@tablation/client";
|
|
6983
|
+
|
|
6984
|
+
// src/contract.ts
|
|
6985
|
+
var DEFAULT_CONTRACT = {
|
|
6986
|
+
columns: {
|
|
6987
|
+
key: "issue_id",
|
|
6988
|
+
tag: "issue_tag",
|
|
6989
|
+
title: "title",
|
|
6990
|
+
status: "status",
|
|
6991
|
+
assignee: "assignee_id",
|
|
6992
|
+
priority: "priority",
|
|
6993
|
+
severity: "severity",
|
|
6994
|
+
slice: "project_id",
|
|
6995
|
+
repo: "repo_id",
|
|
6996
|
+
parent: "parent_id",
|
|
6997
|
+
blockedBy: "blocked_by",
|
|
6998
|
+
updatedAt: "updated_at",
|
|
6999
|
+
needsDesign: "needs_design",
|
|
7000
|
+
attachments: "attachments",
|
|
7001
|
+
workMode: "work_mode",
|
|
7002
|
+
reportType: "report_type",
|
|
7003
|
+
epic: "epic_id",
|
|
7004
|
+
author: "filed_by_id",
|
|
7005
|
+
ship: "ship_id",
|
|
7006
|
+
heldBy: "held_by_ship_id",
|
|
7007
|
+
resolutionNote: "resolution_note",
|
|
7008
|
+
verifiedSha: "verified_sha",
|
|
7009
|
+
prRef: "pr_ref",
|
|
7010
|
+
position: "position"
|
|
7011
|
+
},
|
|
7012
|
+
comments: {
|
|
7013
|
+
parent: "ticket_id",
|
|
7014
|
+
body: "body",
|
|
7015
|
+
author: "team_member_id",
|
|
7016
|
+
createdAt: "created_at",
|
|
7017
|
+
ship: "ship_id",
|
|
7018
|
+
publicReply: "is_public_reply",
|
|
7019
|
+
authorName: "author_name"
|
|
7020
|
+
},
|
|
7021
|
+
statuses: {
|
|
7022
|
+
open: ["new", "accepted", "blocked", "in_progress", "needs_info", "fixed", "qa", "verified"],
|
|
7023
|
+
resolved: [
|
|
7024
|
+
"verified",
|
|
7025
|
+
"closed_deployed",
|
|
7026
|
+
"closed_wont_fix",
|
|
7027
|
+
"closed_duplicate",
|
|
7028
|
+
"closed_completed",
|
|
7029
|
+
"closed_cant_reproduce"
|
|
7030
|
+
],
|
|
7031
|
+
approved: "accepted",
|
|
7032
|
+
parked: "blocked",
|
|
7033
|
+
building: "in_progress",
|
|
7034
|
+
handoff: "fixed",
|
|
7035
|
+
verifying: "qa",
|
|
7036
|
+
verified: "verified",
|
|
7037
|
+
deployed: "closed_deployed",
|
|
7038
|
+
needsHuman: "needs_info",
|
|
7039
|
+
reviewing: null,
|
|
7040
|
+
wontFix: "closed_wont_fix",
|
|
7041
|
+
draft: "draft"
|
|
7042
|
+
},
|
|
7043
|
+
epics: {
|
|
7044
|
+
statusColumn: "status",
|
|
7045
|
+
keyColumn: "epic_id",
|
|
7046
|
+
statuses: { planned: "planned", building: "in_progress", done: "done", cancelled: "cancelled" }
|
|
7047
|
+
},
|
|
7048
|
+
priorityOrder: ["p0", "p1", "p2", "p3"],
|
|
7049
|
+
severityOrder: ["s1", "s2", "s3", "s4"],
|
|
7050
|
+
unknownPriorityRank: 2,
|
|
7051
|
+
coordinatingValue: "coordinating",
|
|
7052
|
+
manualWorkMode: "manual",
|
|
7053
|
+
hybridWorkMode: "hybrid",
|
|
7054
|
+
repoExemptReportTypes: ["coordinating", "question", "investigation"]
|
|
7055
|
+
};
|
|
7056
|
+
function closedStatuses(c) {
|
|
7057
|
+
return c.statuses.resolved.filter((s) => s !== c.statuses.verified);
|
|
7058
|
+
}
|
|
7059
|
+
function resolveContract(override) {
|
|
7060
|
+
if (!override) return DEFAULT_CONTRACT;
|
|
7061
|
+
return {
|
|
7062
|
+
columns: { ...DEFAULT_CONTRACT.columns, ...override.columns },
|
|
7063
|
+
comments: { ...DEFAULT_CONTRACT.comments, ...override.comments },
|
|
7064
|
+
statuses: { ...DEFAULT_CONTRACT.statuses, ...override.statuses },
|
|
7065
|
+
epics: {
|
|
7066
|
+
...DEFAULT_CONTRACT.epics,
|
|
7067
|
+
...override.epics,
|
|
7068
|
+
statuses: { ...DEFAULT_CONTRACT.epics.statuses, ...override.epics?.statuses }
|
|
7069
|
+
},
|
|
7070
|
+
priorityOrder: override.priorityOrder ?? DEFAULT_CONTRACT.priorityOrder,
|
|
7071
|
+
severityOrder: override.severityOrder ?? DEFAULT_CONTRACT.severityOrder,
|
|
7072
|
+
unknownPriorityRank: override.unknownPriorityRank ?? DEFAULT_CONTRACT.unknownPriorityRank,
|
|
7073
|
+
coordinatingValue: override.coordinatingValue ?? DEFAULT_CONTRACT.coordinatingValue,
|
|
7074
|
+
manualWorkMode: override.manualWorkMode ?? DEFAULT_CONTRACT.manualWorkMode,
|
|
7075
|
+
hybridWorkMode: override.hybridWorkMode ?? DEFAULT_CONTRACT.hybridWorkMode,
|
|
7076
|
+
// A workspace overriding `coordinatingValue` alone (renaming the rollup
|
|
7077
|
+
// marker) still gets that renamed value exempted here by default,
|
|
7078
|
+
// without also having to repeat it — only an explicit
|
|
7079
|
+
// `repoExemptReportTypes` overrides this derived default.
|
|
7080
|
+
repoExemptReportTypes: override.repoExemptReportTypes ?? (override.coordinatingValue ? [override.coordinatingValue, "question", "investigation"] : DEFAULT_CONTRACT.repoExemptReportTypes)
|
|
7081
|
+
};
|
|
7082
|
+
}
|
|
7083
|
+
function validateContract(c) {
|
|
7084
|
+
const problems = [];
|
|
7085
|
+
const open = new Set(c.statuses.open);
|
|
7086
|
+
const resolved = new Set(c.statuses.resolved);
|
|
7087
|
+
for (const role of ["approved", "parked", "building", "handoff", "verifying", "needsHuman"]) {
|
|
7088
|
+
const v = c.statuses[role];
|
|
7089
|
+
if (!open.has(v)) problems.push(`statuses.${role} ("${v}") is not listed in statuses.open`);
|
|
7090
|
+
}
|
|
7091
|
+
if (c.statuses.reviewing && !open.has(c.statuses.reviewing)) {
|
|
7092
|
+
problems.push(`statuses.reviewing ("${c.statuses.reviewing}") is not listed in statuses.open`);
|
|
7093
|
+
}
|
|
7094
|
+
if (!resolved.has(c.statuses.deployed)) {
|
|
7095
|
+
problems.push(`statuses.deployed ("${c.statuses.deployed}") is not listed in statuses.resolved`);
|
|
7096
|
+
}
|
|
7097
|
+
if (resolved.has(c.statuses.handoff)) {
|
|
7098
|
+
problems.push(
|
|
7099
|
+
`statuses.handoff ("${c.statuses.handoff}") is listed as resolved \u2014 it means built-but-unchecked, so a ticket blocked by it is still blocked; calling it resolved would build on unverified work`
|
|
7100
|
+
);
|
|
7101
|
+
}
|
|
7102
|
+
if (!resolved.has(c.statuses.wontFix)) {
|
|
7103
|
+
problems.push(`statuses.wontFix ("${c.statuses.wontFix}") is not listed in statuses.resolved`);
|
|
7104
|
+
}
|
|
7105
|
+
if (c.statuses.reviewing && resolved.has(c.statuses.reviewing)) {
|
|
7106
|
+
problems.push(
|
|
7107
|
+
`statuses.reviewing ("${c.statuses.reviewing}") is listed as resolved \u2014 it is unmerged work under review, so a ticket blocked by it is still blocked`
|
|
7108
|
+
);
|
|
7109
|
+
}
|
|
7110
|
+
if (resolved.has(c.statuses.approved)) {
|
|
7111
|
+
problems.push(`statuses.approved ("${c.statuses.approved}") is listed as resolved`);
|
|
7112
|
+
}
|
|
7113
|
+
if (c.statuses.parked === c.statuses.approved) {
|
|
7114
|
+
problems.push("statuses.parked and statuses.approved are the same value \u2014 parking would be a no-op");
|
|
7115
|
+
}
|
|
7116
|
+
const epicValues = Object.values(c.epics.statuses);
|
|
7117
|
+
if (new Set(epicValues).size !== epicValues.length) {
|
|
7118
|
+
problems.push("epics.statuses names the same value twice \u2014 the epic sync could not tell those states apart");
|
|
7119
|
+
}
|
|
7120
|
+
if (c.priorityOrder.length === 0) problems.push("priorityOrder is empty");
|
|
7121
|
+
if (c.severityOrder.length === 0) problems.push("severityOrder is empty");
|
|
7122
|
+
for (const [name, order] of [["priorityOrder", c.priorityOrder], ["severityOrder", c.severityOrder]]) {
|
|
7123
|
+
if (new Set(order).size !== order.length) problems.push(`${name} contains duplicates`);
|
|
7124
|
+
}
|
|
7125
|
+
return problems;
|
|
7126
|
+
}
|
|
7127
|
+
var EVENT_LOG_MISSING = "the Issues table has no event log \u2014 crew writes its audit trail there (CREW-1574). In the app, open the Issues table's Data & lifecycle tab and turn on Event log, then rerun.";
|
|
7128
|
+
|
|
7058
7129
|
// src/work-mode.ts
|
|
7059
7130
|
function projectWorkModes(rows, contract) {
|
|
7060
7131
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -8928,8 +8999,10 @@ async function decideCycle(o) {
|
|
|
8928
8999
|
emit.emit(`${what}: no longer missing from the scheduler PATH \u2014 stop cleared`, { step: "select" });
|
|
8929
9000
|
}
|
|
8930
9001
|
await checkRepoDisks({ state, emit, route, ship });
|
|
9002
|
+
const floorStop = await checkCrewFloor({ state, emit, route, ship });
|
|
9003
|
+
const itemStop = repoStopFor(openShipAttention(state), route);
|
|
8931
9004
|
const selectionInput = {
|
|
8932
|
-
repoStop:
|
|
9005
|
+
repoStop: floorStop ? () => floorStop : itemStop,
|
|
8933
9006
|
workModes,
|
|
8934
9007
|
tickets,
|
|
8935
9008
|
comments,
|
|
@@ -14530,6 +14603,16 @@ var SpawnBreaker = class {
|
|
|
14530
14603
|
}
|
|
14531
14604
|
};
|
|
14532
14605
|
async function runOnePass(o) {
|
|
14606
|
+
try {
|
|
14607
|
+
return await runOnePassUnguarded(o);
|
|
14608
|
+
} catch (e) {
|
|
14609
|
+
if (!isUpgradeRequired(e)) throw e;
|
|
14610
|
+
const emit = o.newEmitter();
|
|
14611
|
+
await raiseFromUpgradeRequired({ state: o.state, emit, route: o.route, ship: o.ship }, e);
|
|
14612
|
+
return { pending: [], started: [], skipped: [] };
|
|
14613
|
+
}
|
|
14614
|
+
}
|
|
14615
|
+
async function runOnePassUnguarded(o) {
|
|
14533
14616
|
if (o.state.isPaused()) return { pending: [], started: [], skipped: [] };
|
|
14534
14617
|
await hydrateApiKeys([o.route], o.getSessionStore);
|
|
14535
14618
|
const emit = o.newEmitter();
|
|
@@ -18312,6 +18395,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
18312
18395
|
const issueLabelById = new Map(engagedTickets.map((t) => [t.id, t.issue_id]));
|
|
18313
18396
|
const todo = await statusTracker.openTickets().then((open) => operatorTodo(open, statusTracker.contract, route.resolved?.operator ?? "")).catch(() => []);
|
|
18314
18397
|
const projects = projectModeLabels(await statusTracker.projectRowsWithModes().catch(() => []));
|
|
18398
|
+
const crewMinVersion = await readCrewFloor(route, cfg.ship) ?? null;
|
|
18315
18399
|
if (flag("json")) {
|
|
18316
18400
|
const repos = await resolvedRepos(route);
|
|
18317
18401
|
const seatIds = route.resolved?.seats ?? {};
|
|
@@ -18371,6 +18455,9 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
18371
18455
|
watermark: state.watermark(),
|
|
18372
18456
|
route: {
|
|
18373
18457
|
name: route.route,
|
|
18458
|
+
// Additive (CREW-2248): the running version and the workspace's floor. `version` stays 1.
|
|
18459
|
+
crewVersion: sentVersion(cfg.ship),
|
|
18460
|
+
crewMinVersion,
|
|
18374
18461
|
// Additive (CREW-1496): each project and its work mode. `version` stays 1.
|
|
18375
18462
|
projects: projects.map((p) => ({ id: p.id, name: p.name, workMode: p.mode })),
|
|
18376
18463
|
dir: route.dir,
|
|
@@ -18561,6 +18648,19 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
18561
18648
|
}
|
|
18562
18649
|
}
|
|
18563
18650
|
const tracker = new Tracker(route, cfg.ship);
|
|
18651
|
+
{
|
|
18652
|
+
const have = sentVersion(cfg.ship);
|
|
18653
|
+
const floor = await readCrewFloor(route, cfg.ship);
|
|
18654
|
+
if (have && floor) {
|
|
18655
|
+
process.stdout.write(
|
|
18656
|
+
`crew version: ${belowFloor(have, floor) ? "WARN " : ""}${have} (workspace requires ${floor})${belowFloor(have, floor) ? " \u2014 update crew (the Crew app, or `npm install -g @tablation/crew`)" : ""}
|
|
18657
|
+
`
|
|
18658
|
+
);
|
|
18659
|
+
} else if (have) {
|
|
18660
|
+
process.stdout.write(`crew version: ${have}${floor === null ? " (workspace sets no minimum)" : ""}
|
|
18661
|
+
`);
|
|
18662
|
+
}
|
|
18663
|
+
}
|
|
18564
18664
|
const contractProblems = validateContract(tracker.contract);
|
|
18565
18665
|
process.stdout.write(
|
|
18566
18666
|
contractProblems.length ? `contract: ${contractProblems.length} problem(s)
|
package/package.json
CHANGED