@mutmutco/cli 4.5.16 → 4.5.17
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/main.cjs +133 -12
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -7401,6 +7401,18 @@ function surfaceInferenceSuffix(inferred) {
|
|
|
7401
7401
|
const alts = inferred.alternatives?.length ? ` Other candidates: ${inferred.alternatives.join(", ")}.` : "";
|
|
7402
7402
|
return ` This pick is low-confidence \u2014 pass --surface <value> to choose explicitly.${alts}`;
|
|
7403
7403
|
}
|
|
7404
|
+
function uncertainSurfaceRefusal(inferred, command = "issue create") {
|
|
7405
|
+
if (!inferred.uncertain) return void 0;
|
|
7406
|
+
const alternatives = inferred.alternatives?.length ? ` Other candidates: ${inferred.alternatives.join(", ")}.` : "";
|
|
7407
|
+
return {
|
|
7408
|
+
message: `${command}: surface inference is uncertain (${inferred.reason}) \u2014 pass --surface <value> to choose explicitly or --no-surface only when the filing is genuinely exempt.${alternatives}`,
|
|
7409
|
+
payload: {
|
|
7410
|
+
code: ERROR_CODES.ERR_MISSING_FLAG,
|
|
7411
|
+
offending_flag: "--surface",
|
|
7412
|
+
expected: ["--surface <value>", "--no-surface for a genuinely exempt filing"]
|
|
7413
|
+
}
|
|
7414
|
+
};
|
|
7415
|
+
}
|
|
7404
7416
|
function surfaceInferenceNote(inferred) {
|
|
7405
7417
|
const base = `mmi-cli: no --surface given \u2014 filed under ${inferred.label} (${inferred.reason}).`;
|
|
7406
7418
|
return inferred.uncertain ? `${base}${surfaceInferenceSuffix(inferred)}
|
|
@@ -7438,7 +7450,9 @@ async function checkSurfaceRequirement(input, deps = {}) {
|
|
|
7438
7450
|
}
|
|
7439
7451
|
if (known.length === 0) return { enforcing: false, taxonomyAbsent: true };
|
|
7440
7452
|
if (labelsCarrySurface(input.labels)) return { enforcing: true };
|
|
7441
|
-
|
|
7453
|
+
const inferred = inferSurface(known, { title: input.title, type: input.type, body: input.body });
|
|
7454
|
+
const refusal = uncertainSurfaceRefusal(inferred, input.command ?? "issue create");
|
|
7455
|
+
return refusal ? { enforcing: true, refusal } : { enforcing: true, inferred };
|
|
7442
7456
|
}
|
|
7443
7457
|
function conflictingSurfaceInputs(surfaceFlag, labels) {
|
|
7444
7458
|
if (!surfaceFlag) return void 0;
|
|
@@ -11076,6 +11090,7 @@ var ITEM_FIELD_VALUES_PAGE = 25;
|
|
|
11076
11090
|
var ITEM_LABELS_PAGE = 25;
|
|
11077
11091
|
var ITEM_ASSIGNEES_PAGE = 10;
|
|
11078
11092
|
var ITEM_PROJECT_ITEMS_PAGE = 50;
|
|
11093
|
+
var LINKED_REPOS_PAGE = 20;
|
|
11079
11094
|
var CONTENT_LABELS_AND_ASSIGNEES = `
|
|
11080
11095
|
labels(first: ${ITEM_LABELS_PAGE}) { pageInfo { hasNextPage } nodes { name } }
|
|
11081
11096
|
assignees(first: ${ITEM_ASSIGNEES_PAGE}) { pageInfo { hasNextPage } nodes { login } }`;
|
|
@@ -11086,6 +11101,7 @@ query($owner: String!, $number: Int!, $after: String, $query: String) {
|
|
|
11086
11101
|
projectV2(number: $number) {
|
|
11087
11102
|
id
|
|
11088
11103
|
title
|
|
11104
|
+
repositories(first: ${LINKED_REPOS_PAGE}) { pageInfo { hasNextPage } nodes { nameWithOwner } }
|
|
11089
11105
|
items(first: 100, after: $after, query: $query) {
|
|
11090
11106
|
pageInfo { hasNextPage endCursor }
|
|
11091
11107
|
nodes {
|
|
@@ -11126,6 +11142,44 @@ query($owner: String!, $number: Int!, $after: String, $query: String) {
|
|
|
11126
11142
|
}
|
|
11127
11143
|
}
|
|
11128
11144
|
}`;
|
|
11145
|
+
var INDEX_LAG_RECOVERY_PAGE = 50;
|
|
11146
|
+
var INDEX_LAG_RECOVERY_ITEMS_PAGE = 10;
|
|
11147
|
+
var INDEX_LAG_RECOVERY_MAX_PAGES = 10;
|
|
11148
|
+
var indexLagRecoveryQuery = (connection) => `
|
|
11149
|
+
query($repoOwner: String!, $repoName: String!, $after: String) {
|
|
11150
|
+
repository(owner: $repoOwner, name: $repoName) {
|
|
11151
|
+
${connection}(first: ${INDEX_LAG_RECOVERY_PAGE}, after: $after, states: OPEN) {
|
|
11152
|
+
pageInfo { hasNextPage endCursor }
|
|
11153
|
+
nodes {
|
|
11154
|
+
__typename
|
|
11155
|
+
id
|
|
11156
|
+
number
|
|
11157
|
+
title
|
|
11158
|
+
url
|
|
11159
|
+
state
|
|
11160
|
+
updatedAt
|
|
11161
|
+
repository { nameWithOwner }${CONTENT_LABELS_AND_ASSIGNEES}
|
|
11162
|
+
projectItems(first: ${INDEX_LAG_RECOVERY_ITEMS_PAGE}) {
|
|
11163
|
+
pageInfo { hasNextPage }
|
|
11164
|
+
nodes {
|
|
11165
|
+
id
|
|
11166
|
+
project { id }
|
|
11167
|
+
fieldValues(first: ${ITEM_FIELD_VALUES_PAGE}) {
|
|
11168
|
+
pageInfo { hasNextPage }
|
|
11169
|
+
nodes {
|
|
11170
|
+
... on ProjectV2ItemFieldSingleSelectValue {
|
|
11171
|
+
name
|
|
11172
|
+
optionId
|
|
11173
|
+
field { ... on ProjectV2SingleSelectField { id name } }
|
|
11174
|
+
}
|
|
11175
|
+
}
|
|
11176
|
+
}
|
|
11177
|
+
}
|
|
11178
|
+
}
|
|
11179
|
+
}
|
|
11180
|
+
}
|
|
11181
|
+
}
|
|
11182
|
+
}`;
|
|
11129
11183
|
var ACTIVE_BOARD_ITEMS_FILTER = "-status:Done -is:closed";
|
|
11130
11184
|
var DOCTOR_BOARD_ITEMS_FILTER = "-status:Done";
|
|
11131
11185
|
var ARCHIVE_BOARD_ITEMS_FILTER = "status:Done is:closed";
|
|
@@ -11349,6 +11403,52 @@ function shouldSkipInactiveBoardNode(node, cfg) {
|
|
|
11349
11403
|
const status = parseSingleSelectFields(node.fieldValues?.nodes, cfg).status?.name;
|
|
11350
11404
|
return status === "Done";
|
|
11351
11405
|
}
|
|
11406
|
+
async function recoverIndexLaggedItems(graphql, projectId, linkedRepos, listed, isInactive, warnings) {
|
|
11407
|
+
const known = new Set(listed.map((node) => node.id));
|
|
11408
|
+
const nodes = [];
|
|
11409
|
+
let partial = false;
|
|
11410
|
+
if (linkedRepos?.pageInfo?.hasNextPage) {
|
|
11411
|
+
warnings.push(`partial board read: index-lag recovery covers only the first ${LINKED_REPOS_PAGE} linked repos`);
|
|
11412
|
+
partial = true;
|
|
11413
|
+
}
|
|
11414
|
+
for (const repo of (linkedRepos?.nodes ?? []).map((node) => node.nameWithOwner).filter((name) => Boolean(name))) {
|
|
11415
|
+
const [repoOwner, repoName2] = repo.split("/");
|
|
11416
|
+
let recoveredHere = 0;
|
|
11417
|
+
for (const connection of ["issues", "pullRequests"]) {
|
|
11418
|
+
let after;
|
|
11419
|
+
let pages = 0;
|
|
11420
|
+
try {
|
|
11421
|
+
do {
|
|
11422
|
+
const page = (await graphql(indexLagRecoveryQuery(connection), { repoOwner, repoName: repoName2, ...after ? { after } : {} })).repository?.[connection];
|
|
11423
|
+
for (const content of page?.nodes ?? []) {
|
|
11424
|
+
const { projectItems, ...contentFields } = content;
|
|
11425
|
+
const item = (projectItems?.nodes ?? []).find((entry) => entry.project?.id === projectId);
|
|
11426
|
+
if (!item?.id || known.has(item.id)) continue;
|
|
11427
|
+
const node = { id: item.id, fieldValues: item.fieldValues, content: contentFields };
|
|
11428
|
+
if (isInactive(node)) continue;
|
|
11429
|
+
known.add(item.id);
|
|
11430
|
+
nodes.push(node);
|
|
11431
|
+
recoveredHere += 1;
|
|
11432
|
+
}
|
|
11433
|
+
after = page?.pageInfo?.hasNextPage ? page.pageInfo.endCursor ?? void 0 : void 0;
|
|
11434
|
+
pages += 1;
|
|
11435
|
+
if (after && pages >= INDEX_LAG_RECOVERY_MAX_PAGES) {
|
|
11436
|
+
warnings.push(`partial board read: index-lag recovery for ${repo} ${connection} capped at ${pages * INDEX_LAG_RECOVERY_PAGE} open items`);
|
|
11437
|
+
partial = true;
|
|
11438
|
+
after = void 0;
|
|
11439
|
+
}
|
|
11440
|
+
} while (after);
|
|
11441
|
+
} catch (e) {
|
|
11442
|
+
warnings.push(`partial board read: index-lag recovery for ${repo} ${connection} failed (${e.message})`);
|
|
11443
|
+
partial = true;
|
|
11444
|
+
}
|
|
11445
|
+
}
|
|
11446
|
+
if (recoveredHere) {
|
|
11447
|
+
warnings.push(`board read: ${recoveredHere} active item(s) from ${repo} were missing from GitHub's project items listing (Projects index lag) \u2014 recovered from their own project membership`);
|
|
11448
|
+
}
|
|
11449
|
+
}
|
|
11450
|
+
return { nodes, partial };
|
|
11451
|
+
}
|
|
11352
11452
|
async function collectBoardItems(cfg, options, deps) {
|
|
11353
11453
|
const client = deps.client ?? defaultGitHubClient();
|
|
11354
11454
|
const currentRepo = await resolveCurrentRepo(options, deps);
|
|
@@ -11361,6 +11461,7 @@ async function collectBoardItems(cfg, options, deps) {
|
|
|
11361
11461
|
let projectId = cfg.projectId;
|
|
11362
11462
|
let partial = false;
|
|
11363
11463
|
let pageCount = 0;
|
|
11464
|
+
let linkedRepos;
|
|
11364
11465
|
do {
|
|
11365
11466
|
try {
|
|
11366
11467
|
const page = await fetchProjectPage(client, cfg, after, itemsQuery);
|
|
@@ -11369,6 +11470,7 @@ async function collectBoardItems(cfg, options, deps) {
|
|
|
11369
11470
|
if (!project2) throw new Error(`project ${cfg.projectOwner}#${cfg.projectNumber} not found`);
|
|
11370
11471
|
projectId = project2.id;
|
|
11371
11472
|
projectTitle ||= project2.title;
|
|
11473
|
+
linkedRepos ??= project2.repositories;
|
|
11372
11474
|
pageCount += 1;
|
|
11373
11475
|
const pageNodes = project2.items.nodes ?? [];
|
|
11374
11476
|
if (options.activeOnly) {
|
|
@@ -11410,6 +11512,18 @@ async function collectBoardItems(cfg, options, deps) {
|
|
|
11410
11512
|
partial = true;
|
|
11411
11513
|
}
|
|
11412
11514
|
}
|
|
11515
|
+
if (options.activeOnly) {
|
|
11516
|
+
const recovered = await recoverIndexLaggedItems(
|
|
11517
|
+
(query, variables) => client.graphql(query, variables),
|
|
11518
|
+
projectId,
|
|
11519
|
+
linkedRepos,
|
|
11520
|
+
nodes,
|
|
11521
|
+
(node) => shouldSkipInactiveBoardNode(node, cfg),
|
|
11522
|
+
warnings
|
|
11523
|
+
);
|
|
11524
|
+
nodes.push(...recovered.nodes);
|
|
11525
|
+
partial ||= recovered.partial;
|
|
11526
|
+
}
|
|
11413
11527
|
const read = nodesToItems(nodes, warnings, cfg);
|
|
11414
11528
|
if (read.failures.length) {
|
|
11415
11529
|
const message2 = `partial board read: ${read.failures.length} item(s) came back truncated and were not read \u2014 ${read.failures.join(" | ")}`;
|
|
@@ -16001,10 +16115,10 @@ var rollout_plan_default = {
|
|
|
16001
16115
|
note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
|
|
16002
16116
|
},
|
|
16003
16117
|
baseline: {
|
|
16004
|
-
version: "4.5.
|
|
16005
|
-
tag: "v4.5.
|
|
16006
|
-
commit: "
|
|
16007
|
-
npm: "@mutmutco/cli@4.5.
|
|
16118
|
+
version: "4.5.17",
|
|
16119
|
+
tag: "v4.5.17",
|
|
16120
|
+
commit: "be54d6077479",
|
|
16121
|
+
npm: "@mutmutco/cli@4.5.17"
|
|
16008
16122
|
},
|
|
16009
16123
|
exitCriterion: "fleet-n-of-n",
|
|
16010
16124
|
hubOnlyShortcut: "forbidden",
|
|
@@ -16021,14 +16135,14 @@ var rollout_plan_default = {
|
|
|
16021
16135
|
repo: "mutmutco/mmi-hub",
|
|
16022
16136
|
role: "canary",
|
|
16023
16137
|
schedule: "train",
|
|
16024
|
-
v3Target: "v4.5.
|
|
16138
|
+
v3Target: "v4.5.17"
|
|
16025
16139
|
}
|
|
16026
16140
|
],
|
|
16027
16141
|
rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
|
|
16028
16142
|
rollback: {
|
|
16029
16143
|
independent: true,
|
|
16030
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
16031
|
-
v3Target: "v4.5.
|
|
16144
|
+
mechanism: "npm dist-tag latest -> 4.5.17 and redeploy the Hub Lambda from tag v4.5.17 (be54d6077479); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
16145
|
+
v3Target: "v4.5.17 (@mutmutco/cli@4.5.17, tag commit be54d6077479 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
16032
16146
|
}
|
|
16033
16147
|
},
|
|
16034
16148
|
{
|
|
@@ -35691,10 +35805,12 @@ function validateBatchSpecs(specs) {
|
|
|
35691
35805
|
}
|
|
35692
35806
|
return { ok: errors.length === 0, errors, validated };
|
|
35693
35807
|
}
|
|
35694
|
-
async function preflightBatchSurfaces(validated, rowRepo, options) {
|
|
35808
|
+
async function preflightBatchSurfaces(validated, rowRepo, options, deps = {}) {
|
|
35809
|
+
const surfaceApplies = deps.surfaceLabelApplies ?? surfaceLabelApplies;
|
|
35810
|
+
const readLabels = deps.readRepoSurfaceLabels ?? readRepoSurfaceLabels;
|
|
35695
35811
|
const applies = /* @__PURE__ */ new Map();
|
|
35696
35812
|
const appliesTo = async (repo) => {
|
|
35697
|
-
if (!applies.has(repo)) applies.set(repo, await
|
|
35813
|
+
if (!applies.has(repo)) applies.set(repo, await surfaceApplies(repo));
|
|
35698
35814
|
return applies.get(repo);
|
|
35699
35815
|
};
|
|
35700
35816
|
if (options.surface) {
|
|
@@ -35709,7 +35825,7 @@ async function preflightBatchSurfaces(validated, rowRepo, options) {
|
|
|
35709
35825
|
const censusFor = (repo) => {
|
|
35710
35826
|
let pending = censusCache.get(repo);
|
|
35711
35827
|
if (!pending) {
|
|
35712
|
-
pending =
|
|
35828
|
+
pending = readLabels(repo);
|
|
35713
35829
|
censusCache.set(repo, pending);
|
|
35714
35830
|
}
|
|
35715
35831
|
return pending;
|
|
@@ -35727,9 +35843,14 @@ async function preflightBatchSurfaces(validated, rowRepo, options) {
|
|
|
35727
35843
|
}
|
|
35728
35844
|
if (known.length === 0) continue;
|
|
35729
35845
|
const inferred = inferSurface(known, { title: spec.title, type: spec.type, body: spec.body });
|
|
35846
|
+
const refusal = uncertainSurfaceRefusal(inferred, `issue create --batch row ${row}`);
|
|
35847
|
+
if (refusal) {
|
|
35848
|
+
errors.push({ row, error: refusal.message });
|
|
35849
|
+
continue;
|
|
35850
|
+
}
|
|
35730
35851
|
spec.labels = [...spec.labels ?? [], inferred.label];
|
|
35731
35852
|
process.stderr.write(
|
|
35732
|
-
`mmi-cli: no --surface given \u2014 row ${row} filed under ${inferred.label} (${inferred.reason}). Pass --surface to choose
|
|
35853
|
+
`mmi-cli: no --surface given \u2014 row ${row} filed under ${inferred.label} (${inferred.reason}). Pass --surface to choose.
|
|
35733
35854
|
`
|
|
35734
35855
|
);
|
|
35735
35856
|
}
|
package/package.json
CHANGED