@kaddo/mcp 3.123.0 → 3.124.1

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/index.js +113 -17
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10403,6 +10403,39 @@ function buildGuardHistory(dir) {
10403
10403
  };
10404
10404
  }
10405
10405
 
10406
+ // ../cli/src/core/reporting-evidence.ts
10407
+ function reportingProjectContext(dir) {
10408
+ const config = loadConfig(dir);
10409
+ if (!config) return { state: "unknown", mode: "unknown", repository_structure: "unknown" };
10410
+ return {
10411
+ state: config.project.state,
10412
+ mode: projectMode(config),
10413
+ repository_structure: config.project.structure
10414
+ };
10415
+ }
10416
+ function knowledgeLayerEvidence(context, layer2, present) {
10417
+ const baseline = context.mode === "poc" ? ["Tech", "Delivery"] : ["Business", "Product", "Tech", "Delivery"];
10418
+ if (!baseline.includes(layer2)) {
10419
+ return { id: `knowledge.${layer2.toLowerCase()}`, label: `${layer2} knowledge`, classification: "not_applicable", reason: "This layer is outside the configured POC baseline." };
10420
+ }
10421
+ return {
10422
+ id: `knowledge.${layer2.toLowerCase()}`,
10423
+ label: `${layer2} knowledge`,
10424
+ classification: present ? "observed" : "unknown",
10425
+ reason: present ? "A structured knowledge artifact is present." : "No structured artifact was found; absence is not proof that knowledge does not exist."
10426
+ };
10427
+ }
10428
+ function deliveryCapabilityEvidence(dir, context) {
10429
+ const initiatives = discoverInitiatives(dir);
10430
+ const resources = getResources(dir);
10431
+ const modules = loadMappedModules(dir);
10432
+ return [
10433
+ { id: "initiatives", label: "Initiatives", classification: initiatives.length ? "observed" : "not_applicable", reason: initiatives.length ? `${initiatives.length} optional Initiative artifact(s) were discovered.` : "Initiatives are optional and no Initiative artifacts were discovered." },
10434
+ { id: "resources", label: "Project Resources", classification: resources.length ? "observed" : "not_applicable", reason: resources.length ? `${resources.length} Project Resource artifact(s) were discovered.` : "Resources are optional and no Resource artifacts were discovered." },
10435
+ { id: "multirepo", label: "Multirepo mapping", classification: context.repository_structure === "multirepo" ? modules.length ? "observed" : "unknown" : "not_applicable", reason: context.repository_structure === "multirepo" ? modules.length ? `${modules.length} module mapping(s) were discovered.` : "Project is multirepo but no module mappings were found." : "The project is configured as a monorepo." }
10436
+ ];
10437
+ }
10438
+
10406
10439
  // ../cli/src/core/impact-report.ts
10407
10440
  function isBroadGlob(glob) {
10408
10441
  if (!glob.endsWith("/**")) return false;
@@ -10422,8 +10455,10 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10422
10455
  const resolvedScope = opts.scope ?? "all";
10423
10456
  const scopeSource = opts.scopeSource ?? (opts.scope ? "explicit" : "default");
10424
10457
  const exp = buildProjectExplanation(dir);
10458
+ const projectContext = reportingProjectContext(dir);
10425
10459
  const wis = discoverWorkItems(dir);
10426
10460
  const total = wis.length;
10461
+ const verifiedWorkItems = wis.filter((wi) => wi.validationStatus === "passed").length;
10427
10462
  let withOwnership = 0;
10428
10463
  let withSource = 0;
10429
10464
  let withInitiative = 0;
@@ -10515,7 +10550,8 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10515
10550
  const knowledge_coverage = [
10516
10551
  { label: "Work Items with ownership", have: withOwnership, total },
10517
10552
  { label: "Work Items with source", have: withSource, total },
10518
- { label: "Work Items with initiative", have: withInitiative, total },
10553
+ // Initiatives are optional: report their observed use, but do not use absence as a gap or score input.
10554
+ { label: "Work Items using optional initiatives", have: withInitiative, total },
10519
10555
  { label: "Work Items with acceptance criteria", have: withAcceptance, total },
10520
10556
  { label: "Work Items with Definition of Done", have: withDoD, total },
10521
10557
  { label: "Work Items with knowledge level", have: withLevel, total }
@@ -10569,7 +10605,8 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10569
10605
  let score_breakdown = null;
10570
10606
  if (total > 0) {
10571
10607
  const healthPts = [layerStatus("Business"), layerStatus("Product"), layerStatus("Tech"), layerStatus("Delivery")].filter((s) => s !== "Missing").length / 4 * 20;
10572
- const covAvg = knowledge_coverage.reduce((a2, c) => a2 + coverageRatio(c.have), 0) / knowledge_coverage.length;
10608
+ const applicableCoverage = knowledge_coverage.filter((c) => c.label !== "Work Items using optional initiatives");
10609
+ const covAvg = applicableCoverage.reduce((a2, c) => a2 + coverageRatio(c.have), 0) / applicableCoverage.length;
10573
10610
  const coveragePts = covAvg * 20;
10574
10611
  const ownPts = coverageRatio(withOwnership) * 15;
10575
10612
  const traceBlend = (exp.roadmap.candidates > 0 ? exp.roadmap.materialized / exp.roadmap.candidates : 1) * 0.5 + coverageRatio(withOwnership) * 0.5;
@@ -10600,7 +10637,6 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10600
10637
  for (const a2 of [
10601
10638
  groupedAction(gaps.missing_code_ownership, "Add `code:` ownership to"),
10602
10639
  groupedAction(gaps.missing_source, "Add `source`/`source_id` to"),
10603
- groupedAction(gaps.missing_initiative, "Add an `initiative` to"),
10604
10640
  groupedAction(gaps.missing_acceptance_criteria, "Add Acceptance Criteria to"),
10605
10641
  groupedAction(gaps.missing_definition_of_done, "Add a Definition of Done to"),
10606
10642
  groupedAction(gaps.missing_validation, "Add a validation (How to test it) section to")
@@ -10619,6 +10655,14 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10619
10655
  return {
10620
10656
  generated_at: now.toISOString(),
10621
10657
  project: exp.project.name,
10658
+ project_context: projectContext,
10659
+ evidence: [
10660
+ ...["Business", "Product", "Tech", "Delivery"].map((layer2) => knowledgeLayerEvidence(projectContext, layer2, layerStatus(layer2) !== "Missing")),
10661
+ { id: "work-items.lifecycle", label: "Work Item lifecycle", classification: total > 0 ? "observed" : "unknown", reason: total > 0 ? `${total} Work Item artifact(s) were discovered.` : "No Work Item artifacts were discovered." },
10662
+ { id: "delivery.verification", label: "Verified delivery outcomes", classification: verifiedWorkItems > 0 ? "observed" : "unknown", reason: verifiedWorkItems > 0 ? `${verifiedWorkItems} Work Item(s) have persisted validation_status: passed.` : "Acceptance Criteria and Definition of Done sections show declared intent, not completed verification evidence." },
10663
+ ...deliveryCapabilityEvidence(dir, projectContext),
10664
+ { id: "guard-history", label: "Guard consistency history", classification: buildGuardHistory(dir).available ? "observed" : "unknown", reason: buildGuardHistory(dir).available ? "Recorded Guard runs provide current consistency evidence." : "No recorded Guard history is available." }
10665
+ ],
10622
10666
  scope: g.scope,
10623
10667
  default_scope: "all",
10624
10668
  scope_source: scopeSource,
@@ -10673,9 +10717,10 @@ function renderImpactMarkdown(r2) {
10673
10717
  L.push("# Kaddo Knowledge Impact Report", "");
10674
10718
  L.push(`Generated at: ${r2.generated_at}`);
10675
10719
  L.push(`Project: ${r2.project}`);
10720
+ L.push(`Project context: state=${r2.project_context.state}; mode=${r2.project_context.mode}; repositories=${r2.project_context.repository_structure}`);
10676
10721
  L.push(`Scope: ${r2.scope}`);
10677
- if (r2.score !== null) L.push(`Knowledge Impact Score: ${r2.score}/100`);
10678
- else L.push("Knowledge Impact Score: not available");
10722
+ if (r2.score !== null) L.push(`Legacy Knowledge Impact Score: ${r2.score}/100 (deprecated; use Evidence and Applicability)`);
10723
+ else L.push("Legacy Knowledge Impact Score: not available");
10679
10724
  if (r2.scope_source === "default") {
10680
10725
  L.push("Scope note: Impact reports use `all` by default to measure accumulated knowledge impact.");
10681
10726
  } else if (r2.scope === "active" && r2.graph_quality.available === false) {
@@ -10685,6 +10730,9 @@ function renderImpactMarkdown(r2) {
10685
10730
  L.push("## Executive Summary", "");
10686
10731
  for (const s2 of r2.executive_summary) L.push(`- ${s2}`);
10687
10732
  L.push("");
10733
+ L.push("## Evidence and Applicability", "");
10734
+ for (const item of r2.evidence) L.push(`- ${item.label}: ${item.classification} \u2014 ${item.reason}`);
10735
+ L.push("");
10688
10736
  L.push("## Knowledge Health", "");
10689
10737
  for (const [k2, v] of Object.entries(r2.knowledge_health)) L.push(`- ${k2}: ${v}`);
10690
10738
  L.push("");
@@ -10856,6 +10904,7 @@ function loadAssumptions(dir) {
10856
10904
  source: "file",
10857
10905
  assumptions: {
10858
10906
  currency: raw?.currency ?? DEFAULT_ASSUMPTIONS.currency,
10907
+ ...typeof raw?.calibrated_at === "string" && raw.calibrated_at.trim() ? { calibrated_at: raw.calibrated_at.trim() } : {},
10859
10908
  hourly_cost: num(raw?.hourly_cost, DEFAULT_ASSUMPTIONS.hourly_cost),
10860
10909
  context_preparation_minutes_saved_per_work_item: num(a2.context_preparation_minutes_saved_per_work_item, DEFAULT_ASSUMPTIONS.context_preparation_minutes_saved_per_work_item),
10861
10910
  rework_hours_avoided_per_resolved_drift: num(a2.rework_hours_avoided_per_resolved_drift, DEFAULT_ASSUMPTIONS.rework_hours_avoided_per_resolved_drift),
@@ -10882,6 +10931,7 @@ var SAVINGS_DISCLAIMER = "These are evidence-based estimates, not exact ROI. Adj
10882
10931
  function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10883
10932
  const { assumptions: a2, source: source3 } = loadAssumptions(dir);
10884
10933
  const impact = buildImpactReport(dir, { scope: opts.scope, scopeSource: opts.scopeSource }, now);
10934
+ const projectContext = reportingProjectContext(dir);
10885
10935
  const completed = impact.traceability.completed_work_items;
10886
10936
  const wiWithOwnership = parseRatioNumerator(impact.traceability.work_items_connected_to_code);
10887
10937
  const acceptanceCov = impact.knowledge_coverage.find((c) => c.label.toLowerCase().includes("acceptance"));
@@ -10913,20 +10963,28 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10913
10963
  reason: "Guard history exists, but no resolved drift warnings have been recorded yet."
10914
10964
  };
10915
10965
  }
10916
- const totalHours = round2(ctxPrepH + reviewH + clarH + onboardH + archH + driftSavings.hours);
10917
- const totalValue = Math.round(totalHours * a2.hourly_cost);
10966
+ const observedHours = round2(ctxPrepH + reviewH + clarH + driftSavings.hours);
10967
+ const projectedHours = round2(onboardH + archH);
10968
+ const observedValue = Math.round(observedHours * a2.hourly_cost);
10969
+ const projectedValue = Math.round(projectedHours * a2.hourly_cost);
10918
10970
  const reasons = [];
10919
10971
  let level;
10920
- if (impact.score === null || impact.score < 60 || graphQuality === "empty") {
10972
+ const verifiedDelivery = impact.evidence.some((item) => item.id === "delivery.verification" && item.classification === "observed");
10973
+ const evidenceStrong = graphQuality === "good" && impact.ownership_coverage.coverage_percent >= 70 && verifiedDelivery;
10974
+ if (!evidenceStrong || graphQuality === "empty") {
10921
10975
  level = "Low";
10922
- reasons.push("Impact score is low or the knowledge graph is empty.");
10923
- } else if (impact.score >= 85 && graphQuality === "good" && source3 === "file" && guard.available && guard.resolved > 0) {
10976
+ reasons.push("Delivery, ownership or graph evidence is insufficient for a reliable estimate.");
10977
+ } else if (evidenceStrong && source3 === "file" && Boolean(a2.calibrated_at) && guard.available && guard.resolved > 0) {
10924
10978
  level = "High";
10925
- reasons.push("Strong evidence, custom assumptions and recorded drift resolution.");
10979
+ reasons.push("Strong evidence, calibrated assumptions and recorded drift resolution.");
10926
10980
  } else {
10927
10981
  level = "Medium";
10928
10982
  reasons.push("Strong knowledge and graph evidence.");
10929
10983
  }
10984
+ if (!a2.calibrated_at && level === "High") {
10985
+ level = "Medium";
10986
+ reasons.push("Uncalibrated assumptions cannot support High confidence.");
10987
+ }
10930
10988
  if (!guard.available) {
10931
10989
  reasons.push("Guard history is not persisted yet \u2014 avoided rework is not estimated.");
10932
10990
  } else if (guard.resolved > 0) {
@@ -10951,7 +11009,15 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10951
11009
  return {
10952
11010
  generated_at: now.toISOString(),
10953
11011
  project: impact.project,
11012
+ project_context: projectContext,
10954
11013
  scope: impact.scope,
11014
+ reporting_period: { observed: "all recorded project history", projected: "per month", note: "Historical estimates and future monthly projections are intentionally not added together." },
11015
+ evidence_classification: [
11016
+ { id: "completed-work-items", label: "Completed Work Items", classification: completed > 0 ? "observed" : "unknown", reason: completed > 0 ? "Lifecycle state is recorded in Work Items." : "No completed Work Items were recorded." },
11017
+ { id: "guard-resolution", label: "Resolved drift warnings", classification: guard.available ? "observed" : "unknown", reason: guard.available ? "Derived from recorded Guard history." : "No Guard history was recorded." },
11018
+ { id: "assumptions", label: "Savings assumptions", classification: "estimated", reason: a2.calibrated_at ? `Project assumptions were calibrated on ${a2.calibrated_at}.` : source3 === "file" ? "Project-provided assumptions are not marked as calibrated." : "Defaults are uncalibrated estimates." },
11019
+ { id: "team-expectations", label: "Future team activity", classification: "projected", reason: "Monthly contributor and Work Item expectations model a future scenario." }
11020
+ ],
10955
11021
  currency: a2.currency,
10956
11022
  disclaimer: SAVINGS_DISCLAIMER,
10957
11023
  assumptions_source: source3,
@@ -10985,7 +11051,11 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10985
11051
  architecture_discovery: { hours: archH, formula: `${a2.architecture_discovery_hours_saved_when_graph_good} h \xD7 ${graphMult.toFixed(2)} graph multiplier` },
10986
11052
  drift_prevention: driftSavings
10987
11053
  },
10988
- total: { estimated_hours_saved: totalHours, estimated_value: totalValue, currency: a2.currency },
11054
+ totals: {
11055
+ observed_history_estimate: { estimated_hours: observedHours, estimated_value: observedValue, currency: a2.currency },
11056
+ monthly_projection: { estimated_hours: projectedHours, estimated_value: projectedValue, currency: a2.currency }
11057
+ },
11058
+ total: { estimated_hours_saved: observedHours, estimated_value: observedValue, currency: a2.currency },
10989
11059
  confidence: { level, reasons },
10990
11060
  suggested_actions: actions
10991
11061
  };
@@ -10995,14 +11065,18 @@ function renderSavingsMarkdown(r2) {
10995
11065
  L.push("# Kaddo Estimated Savings Report", "");
10996
11066
  L.push(`Generated at: ${r2.generated_at}`);
10997
11067
  L.push(`Project: ${r2.project}`);
11068
+ L.push(`Project context: state=${r2.project_context.state}; mode=${r2.project_context.mode}; repositories=${r2.project_context.repository_structure}`);
10998
11069
  L.push(`Scope: ${r2.scope}`);
10999
11070
  L.push(`Currency: ${r2.currency}`);
11000
11071
  L.push("");
11001
11072
  L.push("## Disclaimer", "");
11002
11073
  L.push(r2.disclaimer, "");
11074
+ L.push("## Evidence Classification", "");
11075
+ for (const item of r2.evidence_classification) L.push(`- ${item.label}: ${item.classification} \u2014 ${item.reason}`);
11076
+ L.push("");
11003
11077
  L.push("## Executive Summary", "");
11004
- L.push(`- Estimated time saved: ${r2.total.estimated_hours_saved} hours`);
11005
- L.push(`- Estimated value: ${r2.total.estimated_value} ${r2.currency}`);
11078
+ L.push(`- Historical estimate: ${r2.totals.observed_history_estimate.estimated_hours} hours`);
11079
+ L.push(`- Monthly projection: ${r2.totals.monthly_projection.estimated_hours} hours`);
11006
11080
  L.push(`- Confidence: ${r2.confidence.level}`);
11007
11081
  if (r2.assumptions_source === "default") L.push("- Using default assumptions. Run `kaddo savings init` to customize them.");
11008
11082
  L.push("");
@@ -11055,9 +11129,10 @@ function renderSavingsMarkdown(r2) {
11055
11129
  L.push("- Not available yet.");
11056
11130
  L.push(`- Reason: ${s.drift_prevention.reason}`, "");
11057
11131
  }
11058
- L.push("## Total", "");
11059
- L.push(`- Estimated time saved: ${r2.total.estimated_hours_saved} h`);
11060
- L.push(`- Estimated value: ${r2.total.estimated_value} ${r2.currency}`);
11132
+ L.push("## Total (Period-separated)", "");
11133
+ L.push(`- Historical estimate (all recorded project history): ${r2.totals.observed_history_estimate.estimated_hours} h / ${r2.totals.observed_history_estimate.estimated_value} ${r2.currency}`);
11134
+ L.push(`- Monthly projection: ${r2.totals.monthly_projection.estimated_hours} h / ${r2.totals.monthly_projection.estimated_value} ${r2.currency}`);
11135
+ L.push(`- Note: ${r2.reporting_period.note}`);
11061
11136
  L.push("");
11062
11137
  L.push("## Confidence", "");
11063
11138
  L.push(`Confidence: ${r2.confidence.level}`, "", "Reason:");
@@ -11076,6 +11151,14 @@ function serializeSavingsJson(r2) {
11076
11151
  function buildDriftReport(dir, now = /* @__PURE__ */ new Date()) {
11077
11152
  const h2 = buildGuardHistory(dir);
11078
11153
  const project = loadConfig(dir)?.project.name ?? "unknown";
11154
+ const projectContext = reportingProjectContext(dir);
11155
+ const workItems = discoverWorkItems(dir);
11156
+ const verificationEvidence = workItems.filter((wi) => wi.validationStatus && wi.validationStatus !== "not-started");
11157
+ const plans = getDeprecationPlans(dir);
11158
+ const deprecationFindings = plans.flatMap((plan) => getDeprecationPlan(dir, plan.id)?.targets.flatMap(validateTarget) ?? []);
11159
+ const config = loadConfig(dir);
11160
+ const topology = config ? buildGraph(dir, config, { scope: "all" }, now) : null;
11161
+ const topologyHints = topology ? buildGraphHints(dir, topology, now).summary.hints : null;
11079
11162
  const actions = [];
11080
11163
  if (!h2.available) {
11081
11164
  actions.push("Run `kaddo guard --record` to start recording guard history.");
@@ -11087,6 +11170,13 @@ function buildDriftReport(dir, now = /* @__PURE__ */ new Date()) {
11087
11170
  return {
11088
11171
  generated_at: now.toISOString(),
11089
11172
  project,
11173
+ project_context: projectContext,
11174
+ consistency_evidence: [
11175
+ { id: "guard-current-consistency", label: "Current Guard consistency", classification: h2.available ? "observed" : "unknown", reason: h2.available ? `${h2.total_runs} recorded Guard run(s) provide deterministic consistency evidence.` : "No recorded Guard history is available." },
11176
+ { id: "lifecycle-consistency", label: "Lifecycle evidence consistency", classification: verificationEvidence.length > 0 ? "observed" : "unknown", reason: verificationEvidence.length > 0 ? `${verificationEvidence.length} Work Item(s) persist an implementation validation status.` : "No persisted Work Item validation status is available." },
11177
+ { id: "deprecation-consistency", label: "Deprecation consistency", classification: plans.length === 0 ? "not_applicable" : deprecationFindings.length === 0 ? "derived" : "observed", reason: plans.length === 0 ? "No deprecation plans exist in this project." : deprecationFindings.length === 0 ? `${plans.length} plan(s) passed deterministic target consistency checks.` : `${deprecationFindings.length} deterministic deprecation consistency finding(s) were detected.` },
11178
+ { id: "topology-consistency", label: "Knowledge topology consistency", classification: topology === null ? "unknown" : topologyHints === 0 ? "derived" : "observed", reason: topology === null ? "No project configuration is available to construct the topology." : topologyHints === 0 ? `The in-memory topology has ${topology.nodes.length} node(s) and no open deterministic hints.` : `The in-memory topology has ${topologyHints} open deterministic hint(s).` }
11179
+ ],
11090
11180
  guard_history: {
11091
11181
  available: h2.available,
11092
11182
  total_runs: h2.total_runs,
@@ -11107,6 +11197,10 @@ function renderDriftMarkdown(r2) {
11107
11197
  L.push("# Kaddo Drift Trend Report", "");
11108
11198
  L.push(`Generated at: ${r2.generated_at}`);
11109
11199
  L.push(`Project: ${r2.project}`);
11200
+ L.push(`Project context: state=${r2.project_context.state}; mode=${r2.project_context.mode}; repositories=${r2.project_context.repository_structure}`);
11201
+ L.push("");
11202
+ L.push("## Consistency Evidence", "");
11203
+ for (const item of r2.consistency_evidence) L.push(`- ${item.label}: ${item.classification} \u2014 ${item.reason}`);
11110
11204
  L.push("");
11111
11205
  if (!r2.guard_history.available) {
11112
11206
  L.push("## Guard History", "");
@@ -11174,6 +11268,8 @@ function serializeDriftJson(r2) {
11174
11268
  const out = {
11175
11269
  generated_at: r2.generated_at,
11176
11270
  project: r2.project,
11271
+ project_context: r2.project_context,
11272
+ consistency_evidence: r2.consistency_evidence,
11177
11273
  guard_history: r2.guard_history,
11178
11274
  drift_warnings: r2.drift_warnings,
11179
11275
  hotspots: r2.hotspots,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.123.0",
3
+ "version": "3.124.1",
4
4
  "description": "MCP server for Kaddo project knowledge: read-only resources/tools/prompts plus safe derived generation under .kaddo/.",
5
5
  "license": "MIT",
6
6
  "repository": {