mrvn-cli 0.5.26 → 0.5.27

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.
@@ -20606,6 +20606,15 @@ function analyzeLinkedIssueSignals(linkedIssues, frontmatter, jiraKey, proposedU
20606
20606
  });
20607
20607
  }
20608
20608
  }
20609
+ function computeBlockerProgress(linkedIssues, prerequisiteWeight) {
20610
+ const blockerLinks = linkedIssues.filter(
20611
+ (l) => BLOCKER_LINK_PATTERNS.some((p) => l.relationship.toLowerCase().includes(p.split(" ")[0]))
20612
+ );
20613
+ if (blockerLinks.length === 0) return null;
20614
+ const resolved = blockerLinks.filter((l) => l.isDone).length;
20615
+ const blockerProgress = Math.round(resolved / blockerLinks.length * prerequisiteWeight * 100);
20616
+ return { blockerProgress, totalBlockers: blockerLinks.length, resolvedBlockers: resolved };
20617
+ }
20609
20618
  var LINKED_COMMENT_ANALYSIS_PROMPT = `You are a delivery management assistant analyzing Jira comments from linked issues for progress signals.
20610
20619
 
20611
20620
  For each linked issue below, read the comments and produce a 1-sentence summary focused on: impact on the parent issue, blockers, or decisions.
@@ -21019,6 +21028,39 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
21019
21028
  });
21020
21029
  }
21021
21030
  }
21031
+ const prerequisiteWeight = options.prerequisiteWeight ?? 0.3;
21032
+ const blockerResult = computeBlockerProgress(linkedIssues, prerequisiteWeight);
21033
+ let blockerProgressValue = null;
21034
+ let totalBlockersCount = 0;
21035
+ let resolvedBlockersCount = 0;
21036
+ if (blockerResult && !fm.progressOverride && !DONE_STATUSES6.has(fm.status)) {
21037
+ blockerProgressValue = blockerResult.blockerProgress;
21038
+ totalBlockersCount = blockerResult.totalBlockers;
21039
+ resolvedBlockersCount = blockerResult.resolvedBlockers;
21040
+ const lastProgressUpdate = findLast(proposedUpdates, (u) => u.artifactId === fm.id && u.field === "progress");
21041
+ const implementationProgress = lastProgressUpdate ? lastProgressUpdate.proposedValue : currentProgress;
21042
+ const combinedProgress = Math.round(
21043
+ blockerResult.blockerProgress + implementationProgress * (1 - prerequisiteWeight)
21044
+ );
21045
+ const estimatedProgress = Math.max(currentProgress, combinedProgress);
21046
+ if (estimatedProgress !== currentProgress && estimatedProgress !== implementationProgress) {
21047
+ for (let i = proposedUpdates.length - 1; i >= 0; i--) {
21048
+ if (proposedUpdates[i].artifactId === fm.id && proposedUpdates[i].field === "progress") {
21049
+ proposedUpdates.splice(i, 1);
21050
+ }
21051
+ }
21052
+ proposedUpdates.push({
21053
+ artifactId: fm.id,
21054
+ field: "progress",
21055
+ currentValue: currentProgress,
21056
+ proposedValue: estimatedProgress,
21057
+ reason: `Blocker resolution (${resolvedBlockersCount}/${totalBlockersCount}) + implementation \u2192 dependency-weighted progress ${estimatedProgress}%`
21058
+ });
21059
+ }
21060
+ } else if (blockerResult) {
21061
+ totalBlockersCount = blockerResult.totalBlockers;
21062
+ resolvedBlockersCount = blockerResult.resolvedBlockers;
21063
+ }
21022
21064
  const signals = buildSignals(commentSignals, linkedIssues, statusDrift, proposedMarvinStatus);
21023
21065
  const appliedUpdates = [];
21024
21066
  if (options.applyUpdates && proposedUpdates.length > 0) {
@@ -21061,7 +21103,10 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
21061
21103
  commentAnalysisProgress,
21062
21104
  signals,
21063
21105
  children,
21064
- linkedIssues
21106
+ linkedIssues,
21107
+ blockerProgressValue,
21108
+ totalBlockersCount,
21109
+ resolvedBlockersCount
21065
21110
  );
21066
21111
  const existingHistory = Array.isArray(fm.assessmentHistory) ? fm.assessmentHistory : [];
21067
21112
  const legacySummary = fm.assessmentSummary;
@@ -21110,6 +21155,9 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
21110
21155
  commentAnalysisProgress,
21111
21156
  linkedIssues,
21112
21157
  linkedIssueSignals,
21158
+ blockerProgress: blockerProgressValue,
21159
+ totalBlockers: totalBlockersCount,
21160
+ resolvedBlockers: resolvedBlockersCount,
21113
21161
  children,
21114
21162
  proposedUpdates: options.applyUpdates ? [] : proposedUpdates,
21115
21163
  appliedUpdates,
@@ -21289,6 +21337,9 @@ function emptyArtifactReport(artifactId, errors) {
21289
21337
  commentAnalysisProgress: null,
21290
21338
  linkedIssues: [],
21291
21339
  linkedIssueSignals: [],
21340
+ blockerProgress: null,
21341
+ totalBlockers: 0,
21342
+ resolvedBlockers: 0,
21292
21343
  children: [],
21293
21344
  proposedUpdates: [],
21294
21345
  appliedUpdates: [],
@@ -21296,7 +21347,7 @@ function emptyArtifactReport(artifactId, errors) {
21296
21347
  errors
21297
21348
  };
21298
21349
  }
21299
- function buildAssessmentSummary(commentSummary, commentAnalysisProgress, signals, children, linkedIssues) {
21350
+ function buildAssessmentSummary(commentSummary, commentAnalysisProgress, signals, children, linkedIssues, blockerProgress = null, totalBlockers = 0, resolvedBlockers = 0) {
21300
21351
  const childProgressValues = children.map((c) => {
21301
21352
  const updates = c.appliedUpdates.length > 0 ? c.appliedUpdates : c.proposedUpdates;
21302
21353
  const lastStatus = findLast(updates, (u) => u.field === "status");
@@ -21320,7 +21371,10 @@ function buildAssessmentSummary(commentSummary, commentAnalysisProgress, signals
21320
21371
  childCount: children.length,
21321
21372
  childDoneCount,
21322
21373
  childRollupProgress,
21323
- linkedIssueCount: linkedIssues.length
21374
+ linkedIssueCount: linkedIssues.length,
21375
+ blockerProgress,
21376
+ totalBlockers,
21377
+ resolvedBlockers
21324
21378
  };
21325
21379
  }
21326
21380
  function formatArtifactReport(report) {
@@ -21356,6 +21410,12 @@ function formatArtifactReport(report) {
21356
21410
  }
21357
21411
  parts.push("");
21358
21412
  }
21413
+ if (report.totalBlockers > 0) {
21414
+ parts.push(`## Blocker Resolution`);
21415
+ const bpLabel = report.blockerProgress !== null ? `${report.blockerProgress}%` : "n/a (skipped)";
21416
+ parts.push(` ${report.resolvedBlockers}/${report.totalBlockers} blockers resolved \u2192 ${bpLabel} prerequisite progress`);
21417
+ parts.push("");
21418
+ }
21359
21419
  if (report.children.length > 0) {
21360
21420
  const doneCount = report.children.filter((c) => DONE_STATUSES6.has(c.marvinStatus)).length;
21361
21421
  const childProgress = Math.round(
@@ -22254,10 +22314,11 @@ function createJiraTools(store, projectConfig) {
22254
22314
  // --- Single-artifact assessment ---
22255
22315
  tool20(
22256
22316
  "assess_artifact",
22257
- "Deep assessment of a single Marvin artifact (task, action, or epic). Fetches live Jira status, analyzes comments with LLM, traverses all linked issues, detects drift, rolls up child progress, and extracts contextual signals (blockers, unblocks, handoffs, superseded work).",
22317
+ "Deep assessment of a single Marvin artifact (task, action, or epic). Fetches live Jira status, analyzes comments with LLM, traverses all linked issues, detects drift, rolls up child progress, computes dependency-weighted progress from blocker resolution, and extracts contextual signals (blockers, unblocks, handoffs, superseded work).",
22258
22318
  {
22259
22319
  artifactId: external_exports.string().describe("Marvin artifact ID (e.g. 'T-063', 'A-151', 'E-003')"),
22260
- applyUpdates: external_exports.boolean().optional().describe("Apply proposed status/progress updates to the artifact (default false)")
22320
+ applyUpdates: external_exports.boolean().optional().describe("Apply proposed status/progress updates to the artifact (default false)"),
22321
+ prerequisiteWeight: external_exports.number().min(0).max(1).optional().describe("Weight for blocker-resolution progress signal (0-1, default 0.3). Portion of effort attributed to dependency readiness.")
22261
22322
  },
22262
22323
  async (args) => {
22263
22324
  const jira = createJiraClient(jiraUserConfig);
@@ -22269,6 +22330,7 @@ function createJiraTools(store, projectConfig) {
22269
22330
  {
22270
22331
  artifactId: args.artifactId,
22271
22332
  applyUpdates: args.applyUpdates ?? false,
22333
+ prerequisiteWeight: args.prerequisiteWeight,
22272
22334
  statusMap
22273
22335
  }
22274
22336
  );
@@ -25907,7 +25969,10 @@ function normalizeEntry(entry) {
25907
25969
  childCount: typeof entry.childCount === "number" ? entry.childCount : 0,
25908
25970
  childDoneCount: typeof entry.childDoneCount === "number" ? entry.childDoneCount : 0,
25909
25971
  childRollupProgress: typeof entry.childRollupProgress === "number" ? entry.childRollupProgress : null,
25910
- linkedIssueCount: typeof entry.linkedIssueCount === "number" ? entry.linkedIssueCount : 0
25972
+ linkedIssueCount: typeof entry.linkedIssueCount === "number" ? entry.linkedIssueCount : 0,
25973
+ blockerProgress: typeof entry.blockerProgress === "number" ? entry.blockerProgress : null,
25974
+ totalBlockers: typeof entry.totalBlockers === "number" ? entry.totalBlockers : 0,
25975
+ resolvedBlockers: typeof entry.resolvedBlockers === "number" ? entry.resolvedBlockers : 0
25911
25976
  };
25912
25977
  }
25913
25978
  function renderAssessmentTimeline(history) {
@@ -25927,6 +25992,10 @@ function renderAssessmentTimeline(history) {
25927
25992
  const bar = progressBarHtml(entry.childRollupProgress ?? 0);
25928
25993
  parts.push(`<div class="assessment-stat">\u{1F476} Children: ${entry.childDoneCount}/${entry.childCount} done ${bar} ${entry.childRollupProgress ?? 0}%</div>`);
25929
25994
  }
25995
+ if (entry.totalBlockers > 0) {
25996
+ const bar = progressBarHtml(entry.blockerProgress ?? 0);
25997
+ parts.push(`<div class="assessment-stat">\u{1F6A7} Blockers: ${entry.resolvedBlockers}/${entry.totalBlockers} resolved ${bar} ${entry.blockerProgress ?? 0}%</div>`);
25998
+ }
25930
25999
  if (entry.linkedIssueCount > 0) {
25931
26000
  parts.push(`<div class="assessment-stat">\u{1F517} Linked issues: ${entry.linkedIssueCount}</div>`);
25932
26001
  }