@mutmutco/cli 4.6.40 → 4.6.41

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.
Files changed (2) hide show
  1. package/dist/main.cjs +56 -34
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -22569,7 +22569,10 @@ async function boardDoctor(options, deps = {}) {
22569
22569
  const holderProvenDead = Boolean(
22570
22570
  evidence.marker && evidence.sessionState === "dead" && isHostSessionId(evidence.marker.session) && !evidence.openPr && !evidence.branch
22571
22571
  );
22572
- const freshMarker = evidence.sessionState === "live" || evidence.sessionState !== "dead" && evidence.markerAgeMs !== void 0 && evidence.markerAgeMs < CLAIM_MARKER_LIVE_MS;
22572
+ const freshMarker = evidence.marker ? !claimStampStaleness(evidence.marker, {
22573
+ activityAt: [evidence.lastCommentAt],
22574
+ sessionState: evidence.sessionState
22575
+ }).stale : false;
22573
22576
  const bits = [
22574
22577
  evidence.marker ? `claim marker ${describeClaimMarker(evidence.marker)} ${formatClaimAge(evidence.markerAgeMs)} old` + (evidence.sessionState ? `; local session ${evidence.sessionState}` : "") : evidence.failed.includes("comments") ? "claim marker unreadable" : "no claim marker",
22575
22578
  evidence.openPr,
@@ -23809,6 +23812,9 @@ function parseClaimReleases(body) {
23809
23812
  }
23810
23813
  return out;
23811
23814
  }
23815
+ function isClaimStampComment(body) {
23816
+ return parseClaimMarkers(body).length > 0 || parseClaimReleases(body).length > 0;
23817
+ }
23812
23818
  function renderClaimReleaseBody(release) {
23813
23819
  return `Released by \`${formatSessionIdentity(release)}\` at ${release.ts}.
23814
23820
 
@@ -23859,19 +23865,27 @@ function boardItemClaim(item) {
23859
23865
  function claimStampStaleness(marker, evidence = {}, now = Date.now()) {
23860
23866
  const times = [marker.ts, ...evidence.activityAt ?? []].map((t) => t ? Date.parse(t) : Number.NaN);
23861
23867
  const idleMs = now - Math.max(...times.filter(Number.isFinite));
23862
- return { stale: !evidence.linkedWork && idleMs >= CLAIM_STAMP_IDLE_MS, idleMs };
23868
+ if (evidence.sessionState === "live") return { stale: false, idleMs };
23869
+ if (evidence.linkedWork) return { stale: false, idleMs };
23870
+ return { stale: evidence.sessionState === "dead" || idleMs >= CLAIM_STAMP_IDLE_MS, idleMs };
23863
23871
  }
23864
23872
  function workIsLive(at, now = Date.now()) {
23865
23873
  const idle = now - Date.parse(at ?? "");
23866
23874
  return !Number.isFinite(idle) || idle < CLAIM_STAMP_IDLE_MS;
23867
23875
  }
23868
23876
  function claimFromMarker(item, marker, linked = {}, now = Date.now()) {
23877
+ const sessionState = linked.sessionState ?? probeLocalClaimSession(marker);
23869
23878
  return {
23870
23879
  marker,
23871
23880
  holder: describeClaimMarker(marker),
23872
23881
  ageMs: now - Date.parse(marker.ts),
23873
23882
  currentlyClaimed: item.assignees.length > 0 && item.status === "In Progress",
23874
- ...claimStampStaleness(marker, { activityAt: [...linked.activityAt ?? []], linkedWork: linked.linkedWork }, now)
23883
+ ...sessionState ? { sessionState } : {},
23884
+ ...claimStampStaleness(marker, {
23885
+ activityAt: [...linked.activityAt ?? []],
23886
+ linkedWork: linked.linkedWork,
23887
+ ...sessionState ? { sessionState } : {}
23888
+ }, now)
23875
23889
  };
23876
23890
  }
23877
23891
  function latestClaimMarkerAuthor(comments) {
@@ -23883,14 +23897,15 @@ function latestClaimMarkerAuthor(comments) {
23883
23897
  }
23884
23898
  return void 0;
23885
23899
  }
23886
- function classifyClaimableMarker(comments, actor, now = Date.now(), activityAt = [], viewerLogin) {
23900
+ function classifyClaimableMarker(comments, actor, now = Date.now(), activityAt = [], viewerLogin, sessionState) {
23887
23901
  const marker = comments ? latestClaimMarker(comments) : void 0;
23888
23902
  if (!marker) return { verdict: "claimable" };
23889
23903
  const markerBy = comments ? latestClaimMarkerAuthor(comments) : void 0;
23890
- if (isSeatClaimMarker(marker, actor, { viewerLogin: viewerLogin ?? markerBy, markerBy, now })) {
23904
+ const state = sessionState ?? probeLocalClaimSession(marker);
23905
+ if (isSeatClaimMarker(marker, actor, { viewerLogin: viewerLogin ?? markerBy, markerBy, now, sessionState: state })) {
23891
23906
  return { verdict: "owned", holder: describeClaimMarker(marker) };
23892
23907
  }
23893
- if (!claimStampStaleness(marker, { activityAt }, now).stale) {
23908
+ if (!claimStampStaleness(marker, { activityAt, sessionState: state }, now).stale) {
23894
23909
  return { verdict: "taken", holder: describeClaimMarker(marker) };
23895
23910
  }
23896
23911
  return { verdict: "claimable" };
@@ -23945,10 +23960,12 @@ async function markStaleClaimStamps(scope, readComments, options = {}) {
23945
23960
  markerBy: markerBy ?? (bucket === "userOwned" ? item.assignees[0] : void 0),
23946
23961
  now
23947
23962
  });
23963
+ const sessionState = (options.probeSessionState ?? probeLocalClaimSession)(marker);
23948
23964
  const prs = (options.pullRequests ?? []).filter((pr) => pr.closesIssues.some((ref) => ref.toLowerCase() === item.ref.toLowerCase()));
23949
23965
  item.claim = claimFromMarker(item, marker, {
23950
23966
  activityAt: prs.map((pr) => pr.updatedAt),
23951
- linkedWork: prs.some((pr) => !pr.updatedAt)
23967
+ linkedWork: prs.some((pr) => !pr.updatedAt),
23968
+ ...sessionState ? { sessionState } : {}
23952
23969
  }, now);
23953
23970
  if (ownSeat && bucket !== "userOwned" && bucket !== "claimable") {
23954
23971
  kept.pop();
@@ -24020,9 +24037,25 @@ async function demoteLiveClaimedClaimables(scope, client, actor = describeSessio
24020
24037
  var CLAIM_SESSION_ACTIVITY_MS = 30 * 6e4;
24021
24038
  var CLAIM_SESSION_PROBE_CACHE_MS = 6e4;
24022
24039
  var claimSessionProbeCache = /* @__PURE__ */ new Map();
24023
- function probeLocalClaimSession(marker, now = Date.now()) {
24040
+ function scanSessionTranscripts(root, matches, now) {
24041
+ const pending = [root];
24042
+ while (pending.length) {
24043
+ const dir = pending.pop();
24044
+ for (const entry of (0, import_node_fs18.readdirSync)(dir, { withFileTypes: true })) {
24045
+ const path2 = (0, import_node_path15.join)(dir, entry.name);
24046
+ if (entry.isDirectory()) pending.push(path2);
24047
+ else if (entry.isFile() && matches(entry.name.toLowerCase())) {
24048
+ return now - (0, import_node_fs18.statSync)(path2).mtimeMs <= CLAIM_SESSION_ACTIVITY_MS ? "live" : "dead";
24049
+ }
24050
+ }
24051
+ }
24052
+ return "dead";
24053
+ }
24054
+ function probeLocalClaimSession(marker, now = Date.now(), roots = {}) {
24024
24055
  if (!isHostSessionId(marker.session) || !marker.host || marker.host.toLowerCase() !== (0, import_node_os11.hostname)().toLowerCase()) return void 0;
24025
- if (!marker.surface?.toLowerCase().startsWith("claude")) return void 0;
24056
+ const surface = marker.surface?.toLowerCase() ?? "";
24057
+ const kind = surface.startsWith("claude") ? "claude" : surface.startsWith("jerv") || surface.startsWith("pi") ? "jerv" : void 0;
24058
+ if (!kind) return void 0;
24026
24059
  const cacheKey = `${marker.host.toLowerCase()}/${marker.session}`;
24027
24060
  const cached = claimSessionProbeCache.get(cacheKey);
24028
24061
  if (cached && now - cached.checkedAt <= CLAIM_SESSION_PROBE_CACHE_MS) return cached.state;
@@ -24030,21 +24063,10 @@ function probeLocalClaimSession(marker, now = Date.now()) {
24030
24063
  claimSessionProbeCache.set(cacheKey, { checkedAt: now, state });
24031
24064
  return state;
24032
24065
  };
24033
- const root = (0, import_node_path15.join)((0, import_node_os11.homedir)(), ".claude", "projects");
24034
- try {
24035
- const wanted = `${marker.session}.jsonl`.toLowerCase();
24036
- const pending = [root];
24037
- while (pending.length) {
24038
- const dir = pending.pop();
24039
- for (const entry of (0, import_node_fs18.readdirSync)(dir, { withFileTypes: true })) {
24040
- const path2 = (0, import_node_path15.join)(dir, entry.name);
24041
- if (entry.isDirectory()) pending.push(path2);
24042
- else if (entry.isFile() && entry.name.toLowerCase() === wanted) {
24043
- return remember(now - (0, import_node_fs18.statSync)(path2).mtimeMs <= CLAIM_SESSION_ACTIVITY_MS ? "live" : "dead");
24044
- }
24045
- }
24046
- }
24047
- return remember("dead");
24066
+ try {
24067
+ const session = marker.session.toLowerCase();
24068
+ const state = kind === "claude" ? scanSessionTranscripts(roots.claude ?? (0, import_node_path15.join)((0, import_node_os11.homedir)(), ".claude", "projects"), (name) => name === `${session}.jsonl`, now) : scanSessionTranscripts(roots.jerv ?? (0, import_node_path15.join)((0, import_node_os11.homedir)(), ".jerv", "agent", "sessions"), (name) => name.endsWith(`_${session}.jsonl`), now);
24069
+ return remember(state);
24048
24070
  } catch {
24049
24071
  return remember("unverifiable");
24050
24072
  }
@@ -24092,7 +24114,7 @@ async function gatherClaimLiveness(client, repo, number, fetchOpenPulls) {
24092
24114
  out.marker = latestClaimMarker(rows);
24093
24115
  if (!out.marker) out.releasedAt = latestClaimRelease(rows)?.ts;
24094
24116
  for (let i = comments.length - 1; i >= 0; i -= 1) {
24095
- if (comments[i].created_at && !latestClaimMarker([rows[i]])) {
24117
+ if (comments[i].created_at && !isClaimStampComment(rows[i].body)) {
24096
24118
  out.lastCommentAt = comments[i].created_at;
24097
24119
  break;
24098
24120
  }
@@ -24167,7 +24189,7 @@ function liveEvidenceLines(evidence, repo) {
24167
24189
  if (evidence.marker && evidence.sessionState === "live") {
24168
24190
  live.push(`claim session ${describeClaimMarker(evidence.marker)} has recent local transcript activity`);
24169
24191
  } else if (evidence.marker && evidence.sessionState !== "dead") {
24170
- const { stale, idleMs } = claimStampStaleness(evidence.marker, { activityAt: [evidence.lastCommentAt] });
24192
+ const { stale, idleMs } = claimStampStaleness(evidence.marker, { activityAt: [evidence.lastCommentAt], sessionState: evidence.sessionState });
24171
24193
  const marker = describeClaimMarker(evidence.marker);
24172
24194
  if (!stale) {
24173
24195
  live.push(evidence.lastCommentAt && Date.parse(evidence.lastCommentAt) > Date.parse(evidence.marker.ts) ? `claim marker from ${marker} is ${formatClaimAge(evidence.markerAgeMs)} old but the issue was commented ${formatClaimAge(idleMs)} ago (session not locally probeable)` : `claim marker from ${marker} is only ${formatClaimAge(evidence.markerAgeMs)} old (session not locally probeable)`);
@@ -24261,7 +24283,7 @@ async function checkLaneContest(client, item, actor = describeSessionIdentity(),
24261
24283
  }
24262
24284
  }
24263
24285
  const carry = staleWorkLines(evidence);
24264
- const staleStamp = !contested && !resume && !ownSeat && evidence.marker ? `stale claim stamp ${describeClaimMarker(evidence.marker)} (idle ${formatClaimAge(claimStampStaleness(evidence.marker, { activityAt: [evidence.lastCommentAt] }).idleMs)})${carry.length ? `; continue its ${carry.join(" and ")}` : ""}` : void 0;
24286
+ const staleStamp = !contested && !resume && !ownSeat && evidence.marker ? `stale claim stamp ${describeClaimMarker(evidence.marker)} (idle ${formatClaimAge(claimStampStaleness(evidence.marker, { activityAt: [evidence.lastCommentAt], sessionState: evidence.sessionState }).idleMs)})${carry.length ? `; continue its ${carry.join(" and ")}` : ""}` : void 0;
24265
24287
  return {
24266
24288
  contested,
24267
24289
  live,
@@ -28505,10 +28527,10 @@ var rollout_plan_default = {
28505
28527
  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)."
28506
28528
  },
28507
28529
  baseline: {
28508
- version: "4.6.40",
28509
- tag: "v4.6.40",
28510
- commit: "0c850872865f",
28511
- npm: "@mutmutco/cli@4.6.40"
28530
+ version: "4.6.41",
28531
+ tag: "v4.6.41",
28532
+ commit: "2cf5b22abb64",
28533
+ npm: "@mutmutco/cli@4.6.41"
28512
28534
  },
28513
28535
  exitCriterion: "fleet-n-of-n",
28514
28536
  hubOnlyShortcut: "forbidden",
@@ -28525,14 +28547,14 @@ var rollout_plan_default = {
28525
28547
  repo: "mutmutco/mmi-hub",
28526
28548
  role: "canary",
28527
28549
  schedule: "train",
28528
- v3Target: "v4.6.40"
28550
+ v3Target: "v4.6.41"
28529
28551
  }
28530
28552
  ],
28531
28553
  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.",
28532
28554
  rollback: {
28533
28555
  independent: true,
28534
- mechanism: "npm dist-tag latest -> 4.6.40 and redeploy the Hub Lambda from tag v4.6.40 (0c850872865f); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28535
- v3Target: "v4.6.40 (@mutmutco/cli@4.6.40, tag commit 0c850872865f \u2014 last known-good release carrying the repo-index v4-only contract)"
28556
+ mechanism: "npm dist-tag latest -> 4.6.41 and redeploy the Hub Lambda from tag v4.6.41 (2cf5b22abb64); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28557
+ v3Target: "v4.6.41 (@mutmutco/cli@4.6.41, tag commit 2cf5b22abb64 \u2014 last known-good release carrying the repo-index v4-only contract)"
28536
28558
  }
28537
28559
  },
28538
28560
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.6.40",
3
+ "version": "4.6.41",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",