@kaddo/mcp 3.124.1 → 3.124.3
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/index.js +196 -29
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10404,6 +10404,20 @@ function buildGuardHistory(dir) {
|
|
|
10404
10404
|
}
|
|
10405
10405
|
|
|
10406
10406
|
// ../cli/src/core/reporting-evidence.ts
|
|
10407
|
+
function graphHintFinding(hint) {
|
|
10408
|
+
const affected = [hint.artifact_id, ...hint.path ? [hint.path] : []];
|
|
10409
|
+
return {
|
|
10410
|
+
id: `graph-hint:${hint.artifact_id}:${hint.missing.join(",")}`,
|
|
10411
|
+
category: "topology",
|
|
10412
|
+
source: "graph-hints",
|
|
10413
|
+
affected_artifacts: affected,
|
|
10414
|
+
// Graph Hints are intentionally informational; review is useful but not a blocking error.
|
|
10415
|
+
priority: "medium",
|
|
10416
|
+
evidence: hint.reason,
|
|
10417
|
+
explanation: hint.message,
|
|
10418
|
+
suggested_action: `Review the declared relationships for ${hint.artifact_id}${hint.path ? ` at ${hint.path}` : ""}.`
|
|
10419
|
+
};
|
|
10420
|
+
}
|
|
10407
10421
|
function reportingProjectContext(dir) {
|
|
10408
10422
|
const config = loadConfig(dir);
|
|
10409
10423
|
if (!config) return { state: "unknown", mode: "unknown", repository_structure: "unknown" };
|
|
@@ -10458,7 +10472,11 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10458
10472
|
const projectContext = reportingProjectContext(dir);
|
|
10459
10473
|
const wis = discoverWorkItems(dir);
|
|
10460
10474
|
const total = wis.length;
|
|
10461
|
-
const
|
|
10475
|
+
const completedWis = wis.filter((wi) => wi.lifecycle === "completed");
|
|
10476
|
+
const verifiedWorkItems = completedWis.filter((wi) => wi.validationStatus === "passed").length;
|
|
10477
|
+
const failedValidationWorkItems = completedWis.filter((wi) => wi.validationStatus === "failed").length;
|
|
10478
|
+
const pendingValidationWorkItems = completedWis.filter((wi) => Boolean(wi.validationStatus) && !["passed", "failed"].includes(wi.validationStatus)).length;
|
|
10479
|
+
const notRecordedValidationWorkItems = completedWis.length - verifiedWorkItems - failedValidationWorkItems - pendingValidationWorkItems;
|
|
10462
10480
|
let withOwnership = 0;
|
|
10463
10481
|
let withSource = 0;
|
|
10464
10482
|
let withInitiative = 0;
|
|
@@ -10466,7 +10484,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10466
10484
|
let withAcceptance = 0;
|
|
10467
10485
|
let withDoD = 0;
|
|
10468
10486
|
let withValidation = 0;
|
|
10469
|
-
|
|
10487
|
+
const connectedRoadmapCandidates = /* @__PURE__ */ new Set();
|
|
10470
10488
|
const globCounts = /* @__PURE__ */ new Map();
|
|
10471
10489
|
const globOwners = /* @__PURE__ */ new Map();
|
|
10472
10490
|
const gaps = {
|
|
@@ -10490,13 +10508,17 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10490
10508
|
for (const wi of wis) {
|
|
10491
10509
|
const id = wi.id || wi.title;
|
|
10492
10510
|
if (wi.codeGlobs.length > 0) withOwnership++;
|
|
10493
|
-
else
|
|
10494
|
-
|
|
10511
|
+
else if (wi.type === "bugfix" || wi.type === "hotfix") {
|
|
10512
|
+
gaps.missing_code_ownership.push(gapItem(wi, "Add `code:` globs to connect this corrective Work Item to source paths (`kaddo owners suggest`)."));
|
|
10513
|
+
}
|
|
10514
|
+
const roadmapOrigin = wi.source === "roadmap";
|
|
10515
|
+
const hasSource = Boolean(wi.source) || Boolean(wi.sourceId);
|
|
10495
10516
|
if (hasSource) withSource++;
|
|
10496
|
-
|
|
10497
|
-
|
|
10517
|
+
if (roadmapOrigin && !wi.sourceId) {
|
|
10518
|
+
gaps.missing_source.push(gapItem(wi, "Add `source_id` for this roadmap-originated Work Item."));
|
|
10519
|
+
}
|
|
10520
|
+
if (roadmapOrigin && wi.sourceId) connectedRoadmapCandidates.add(wi.sourceId);
|
|
10498
10521
|
if (wi.initiative) withInitiative++;
|
|
10499
|
-
else gaps.missing_initiative.push(gapItem(wi, "Add `initiative` to connect this Work Item to a delivery initiative."));
|
|
10500
10522
|
if (wi.knowledgeLevel) withLevel++;
|
|
10501
10523
|
else gaps.missing_knowledge_level.push(gapItem(wi, "Add `knowledge_level` (K0\u2013K4) to the front matter."));
|
|
10502
10524
|
for (const g2 of wi.codeGlobs) {
|
|
@@ -10518,18 +10540,21 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10518
10540
|
}
|
|
10519
10541
|
}
|
|
10520
10542
|
const ownedCodePaths = [...globCounts.values()].reduce((a2, b2) => a2 + b2, 0);
|
|
10543
|
+
const connectedRoadmap = connectedRoadmapCandidates.size;
|
|
10521
10544
|
const broadGlobs = [...globCounts.keys()].filter((g2) => isBroadGlob(g2)).length;
|
|
10522
10545
|
for (const [glob, owners] of globOwners) {
|
|
10523
10546
|
if (owners.length > 1) {
|
|
10524
|
-
gaps.ownership_overlaps.push({ code_path: glob, work_items: [...new Set(owners)], suggested_action: "
|
|
10547
|
+
gaps.ownership_overlaps.push({ code_path: glob, work_items: [...new Set(owners)], suggested_action: "Shared historical modification path; it is not a conflict unless incompatible ownership is documented." });
|
|
10525
10548
|
}
|
|
10526
10549
|
}
|
|
10527
10550
|
const ownershipOverlaps = gaps.ownership_overlaps.length;
|
|
10528
10551
|
let g;
|
|
10552
|
+
let graphHintEntries = [];
|
|
10529
10553
|
const config = loadConfig(dir);
|
|
10530
10554
|
if (config) {
|
|
10531
10555
|
const graph = buildGraph(dir, config, { scope: resolvedScope }, now);
|
|
10532
10556
|
const hints = buildGraphHints(dir, graph, now);
|
|
10557
|
+
graphHintEntries = hints.hints;
|
|
10533
10558
|
g = { available: true, scope: graph.scope, scopeReason: graph.scope_reason, nodes: graph.nodes.length, edges: graph.edges.length, quality: hints.quality, hints: hints.summary.hints, generatedAt: graph.generated_at };
|
|
10534
10559
|
} else {
|
|
10535
10560
|
g = { available: false, scope: resolvedScope, scopeReason: "", nodes: 0, edges: 0, quality: "unknown", hints: 0, generatedAt: "" };
|
|
@@ -10593,28 +10618,62 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10593
10618
|
const deliveryTrace = levelFromRatio(
|
|
10594
10619
|
coverageRatio(withOwnership) * 0.5 + (g.available && g.edges > 0 ? 1 : 0) * 0.25 + (exp.roadmap.present ? 1 : 0) * 0.25
|
|
10595
10620
|
);
|
|
10621
|
+
const guardHistory = buildGuardHistory(dir);
|
|
10596
10622
|
const impact_signals = {
|
|
10597
10623
|
ambiguity_reduction: levelFromRatio(ambiguityRatio),
|
|
10598
|
-
drift_prevention:
|
|
10624
|
+
drift_prevention: guardHistory.available && guardHistory.resolved > 0 ? "Observed" : "Unknown",
|
|
10599
10625
|
onboarding_support: onboarding,
|
|
10600
10626
|
delivery_traceability: deliveryTrace,
|
|
10601
10627
|
ai_context_readiness: aiReady,
|
|
10602
10628
|
maintenance_readiness: levelFromRatio((coverageRatio(withOwnership) + (deliveryTraceable ? 1 : 0)) / 2)
|
|
10603
10629
|
};
|
|
10630
|
+
const baselineLayers = projectContext.mode === "poc" ? ["Tech", "Delivery"] : ["Business", "Product", "Tech", "Delivery"];
|
|
10631
|
+
const findings = [];
|
|
10632
|
+
for (const layer2 of baselineLayers) {
|
|
10633
|
+
const status = layerStatus(layer2);
|
|
10634
|
+
if (status !== "Weak" && status !== "Missing") continue;
|
|
10635
|
+
findings.push({
|
|
10636
|
+
id: `knowledge-health:${layer2.toLowerCase()}`,
|
|
10637
|
+
category: "knowledge",
|
|
10638
|
+
source: "knowledge-health",
|
|
10639
|
+
affected_artifacts: [layer2],
|
|
10640
|
+
priority: status === "Missing" ? "high" : "medium",
|
|
10641
|
+
evidence: `${layer2} knowledge maturity is ${status}.`,
|
|
10642
|
+
explanation: `This assesses documented ${layer2} knowledge, not the quality of the product or implementation.`,
|
|
10643
|
+
suggested_action: `Review ${layer2} knowledge coverage and the artifacts or relationships available for it.`
|
|
10644
|
+
});
|
|
10645
|
+
}
|
|
10646
|
+
for (const gap of gaps.missing_code_ownership) {
|
|
10647
|
+
findings.push({
|
|
10648
|
+
id: `ownership:${gap.id}`,
|
|
10649
|
+
category: "delivery",
|
|
10650
|
+
source: "work-item-ownership",
|
|
10651
|
+
affected_artifacts: [gap.id, gap.path],
|
|
10652
|
+
priority: "medium",
|
|
10653
|
+
evidence: "A corrective Work Item has no declared code ownership.",
|
|
10654
|
+
explanation: gap.title,
|
|
10655
|
+
suggested_action: gap.suggested_action
|
|
10656
|
+
});
|
|
10657
|
+
}
|
|
10658
|
+
findings.push(...graphHintEntries.map(graphHintFinding));
|
|
10659
|
+
const priorityOrder = { high: 0, medium: 1, low: 2, unknown: 3 };
|
|
10660
|
+
findings.sort((a2, b2) => priorityOrder[a2.priority] - priorityOrder[b2.priority] || a2.id.localeCompare(b2.id));
|
|
10604
10661
|
let score = null;
|
|
10605
10662
|
let score_breakdown = null;
|
|
10606
10663
|
if (total > 0) {
|
|
10607
|
-
const
|
|
10664
|
+
const layerMaturity = { Consolidated: 1, Structured: 0.8, Traceable: 0.75, Weak: 0.4, Missing: 0 };
|
|
10665
|
+
const healthPts = [layerStatus("Business"), layerStatus("Product"), layerStatus("Tech"), layerStatus("Delivery")].reduce((sum, status) => sum + (layerMaturity[status] ?? 0), 0) / 4 * 20;
|
|
10608
10666
|
const applicableCoverage = knowledge_coverage.filter((c) => c.label !== "Work Items using optional initiatives");
|
|
10609
10667
|
const covAvg = applicableCoverage.reduce((a2, c) => a2 + coverageRatio(c.have), 0) / applicableCoverage.length;
|
|
10610
10668
|
const coveragePts = covAvg * 20;
|
|
10611
10669
|
const ownPts = coverageRatio(withOwnership) * 15;
|
|
10612
|
-
const
|
|
10670
|
+
const roadmapTraceRatio = exp.roadmap.candidates > 0 ? Math.min(connectedRoadmap, exp.roadmap.candidates) / exp.roadmap.candidates : null;
|
|
10671
|
+
const traceSignals = [coverageRatio(withOwnership), ...roadmapTraceRatio === null ? [] : [roadmapTraceRatio]];
|
|
10672
|
+
const traceBlend = traceSignals.reduce((sum, value) => sum + value, 0) / traceSignals.length;
|
|
10613
10673
|
const tracePts = traceBlend * 20;
|
|
10614
10674
|
const qualityMap = { good: 1, partial: 0.6, sparse: 0.3, empty: 0, unknown: 0 };
|
|
10615
10675
|
const graphPts = (g.available ? qualityMap[g.quality] ?? 0 : 0) * 15;
|
|
10616
10676
|
const ctxPts = { Low: 0.25, Medium: 0.5, High: 0.8, "Very High": 1 }[ctxLevel] * 10;
|
|
10617
|
-
score = Math.round(healthPts + coveragePts + ownPts + tracePts + graphPts + ctxPts);
|
|
10618
10677
|
score_breakdown = {
|
|
10619
10678
|
knowledge_health: { points: Math.round(healthPts), max: 20 },
|
|
10620
10679
|
knowledge_coverage: { points: Math.round(coveragePts), max: 20 },
|
|
@@ -10623,6 +10682,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10623
10682
|
graph_quality: { points: Math.round(graphPts), max: 15 },
|
|
10624
10683
|
context_readiness: { points: Math.round(ctxPts), max: 10 }
|
|
10625
10684
|
};
|
|
10685
|
+
score = Object.values(score_breakdown).reduce((sum, component) => sum + component.points, 0);
|
|
10626
10686
|
}
|
|
10627
10687
|
const idsOf = (items) => items.map((i) => i.id);
|
|
10628
10688
|
const groupedAction = (items, verb) => {
|
|
@@ -10636,16 +10696,18 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10636
10696
|
if (exp.roadmap.remaining > 0) actions.push(`Materialize the ${exp.roadmap.remaining} remaining roadmap candidate(s) with \`kaddo create --from roadmap\`.`);
|
|
10637
10697
|
for (const a2 of [
|
|
10638
10698
|
groupedAction(gaps.missing_code_ownership, "Add `code:` ownership to"),
|
|
10639
|
-
groupedAction(gaps.missing_source, "Add `source`/`source_id` to")
|
|
10640
|
-
groupedAction(gaps.missing_acceptance_criteria, "Add Acceptance Criteria to"),
|
|
10641
|
-
groupedAction(gaps.missing_definition_of_done, "Add a Definition of Done to"),
|
|
10642
|
-
groupedAction(gaps.missing_validation, "Add a validation (How to test it) section to")
|
|
10699
|
+
groupedAction(gaps.missing_source, "Add `source`/`source_id` to")
|
|
10643
10700
|
]) if (a2) actions.push(a2);
|
|
10644
10701
|
if (gaps.broad_ownership_globs.length > 0) actions.push(`Narrow ${gaps.broad_ownership_globs.length} broad ownership glob(s) into specific paths.`);
|
|
10645
|
-
|
|
10702
|
+
for (const finding of findings) {
|
|
10703
|
+
if (!actions.includes(finding.suggested_action)) actions.push(finding.suggested_action);
|
|
10704
|
+
}
|
|
10646
10705
|
if (skills.length === 0) actions.push("Install reusable skills with `kaddo add skills`.");
|
|
10647
10706
|
actions.push("Run `kaddo guard` before committing to catch knowledge drift.");
|
|
10648
10707
|
const summary = [];
|
|
10708
|
+
if (completedWis.length > 0) {
|
|
10709
|
+
summary.push(`Delivery evidence: ${completedWis.length} completed; ${verifiedWorkItems} validation passed; ${notRecordedValidationWorkItems} not confirmed by persisted validation.`);
|
|
10710
|
+
}
|
|
10649
10711
|
if (total > 0) summary.push(`${withOwnership}/${total} Work Items include code ownership.`);
|
|
10650
10712
|
if (g.available) summary.push(`The knowledge graph is ${g.quality} with ${g.nodes} nodes and ${g.edges} edges (${g.scope} scope).`);
|
|
10651
10713
|
else summary.push("The knowledge graph has not been exported yet.");
|
|
@@ -10661,12 +10723,21 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10661
10723
|
{ 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
10724
|
{ 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
10725
|
...deliveryCapabilityEvidence(dir, projectContext),
|
|
10664
|
-
{ id: "guard-history", label: "Guard consistency history", classification:
|
|
10726
|
+
{ id: "guard-history", label: "Guard consistency history", classification: guardHistory.available ? "observed" : "unknown", reason: guardHistory.available ? "Recorded Guard runs provide current consistency evidence." : "No recorded Guard history is available." }
|
|
10665
10727
|
],
|
|
10666
10728
|
scope: g.scope,
|
|
10667
10729
|
default_scope: "all",
|
|
10668
10730
|
scope_source: scopeSource,
|
|
10669
10731
|
executive_summary: summary,
|
|
10732
|
+
delivery_evidence: {
|
|
10733
|
+
completed: completedWis.length,
|
|
10734
|
+
validation_passed: verifiedWorkItems,
|
|
10735
|
+
validation_failed: failedValidationWorkItems,
|
|
10736
|
+
validation_pending: pendingValidationWorkItems,
|
|
10737
|
+
validation_not_recorded: notRecordedValidationWorkItems,
|
|
10738
|
+
completion_with_passed_validation_percent: completedWis.length > 0 ? Math.round(verifiedWorkItems / completedWis.length * 1e3) / 10 : null
|
|
10739
|
+
},
|
|
10740
|
+
findings,
|
|
10670
10741
|
knowledge_health,
|
|
10671
10742
|
knowledge_coverage,
|
|
10672
10743
|
ownership_coverage: {
|
|
@@ -10699,7 +10770,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10699
10770
|
},
|
|
10700
10771
|
graph_quality: g.available ? { available: true, scope: g.scope, quality: g.quality, nodes: g.nodes, edges: g.edges, hints: g.hints, reason: g.scopeReason, last_exported: g.generatedAt } : { available: false, suggestion: "Run `kaddo graph export --scope all`." },
|
|
10701
10772
|
guard_activity: (() => {
|
|
10702
|
-
const gh =
|
|
10773
|
+
const gh = guardHistory;
|
|
10703
10774
|
return gh.available ? { available: true, runs_recorded: gh.total_runs, detected: gh.detected, open: gh.open, resolved: gh.resolved, resolution_rate: gh.resolution_rate } : { available: false, note: "Guard history is not persisted. Run `kaddo guard --record` to record runs for trend analysis." };
|
|
10704
10775
|
})(),
|
|
10705
10776
|
impact_signals,
|
|
@@ -10730,6 +10801,27 @@ function renderImpactMarkdown(r2) {
|
|
|
10730
10801
|
L.push("## Executive Summary", "");
|
|
10731
10802
|
for (const s2 of r2.executive_summary) L.push(`- ${s2}`);
|
|
10732
10803
|
L.push("");
|
|
10804
|
+
L.push("## Key Findings", "");
|
|
10805
|
+
if (r2.findings.length === 0) L.push("No current findings detected by the executed deterministic checks.");
|
|
10806
|
+
if (r2.findings.length > 0) {
|
|
10807
|
+
const cell = (value) => value.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
10808
|
+
L.push("| Priority | Source | Affected | Finding | Suggested action |");
|
|
10809
|
+
L.push("|---|---|---|---|---|");
|
|
10810
|
+
for (const finding of r2.findings) {
|
|
10811
|
+
L.push(`| ${finding.priority} | ${cell(finding.source)} | ${cell(finding.affected_artifacts.join(", ") || "not available")} | ${cell(finding.explanation)} Evidence: ${cell(finding.evidence)} | ${cell(finding.suggested_action)} |`);
|
|
10812
|
+
}
|
|
10813
|
+
}
|
|
10814
|
+
L.push("");
|
|
10815
|
+
L.push("## Delivery Evidence", "");
|
|
10816
|
+
const de = r2.delivery_evidence;
|
|
10817
|
+
L.push(`- Completed Work Items: ${de.completed}`);
|
|
10818
|
+
L.push(`- Validation passed: ${de.validation_passed}`);
|
|
10819
|
+
L.push(`- Validation failed: ${de.validation_failed}`);
|
|
10820
|
+
L.push(`- Validation pending: ${de.validation_pending}`);
|
|
10821
|
+
L.push(`- Validation not recorded: ${de.validation_not_recorded}`);
|
|
10822
|
+
L.push(`- Completion with passed validation: ${de.completion_with_passed_validation_percent === null ? "not available" : `${de.completion_with_passed_validation_percent}%`}`);
|
|
10823
|
+
L.push("- \u201CNot recorded\u201D means persisted evidence is absent; it does not prove validation never happened.");
|
|
10824
|
+
L.push("");
|
|
10733
10825
|
L.push("## Evidence and Applicability", "");
|
|
10734
10826
|
for (const item of r2.evidence) L.push(`- ${item.label}: ${item.classification} \u2014 ${item.reason}`);
|
|
10735
10827
|
L.push("");
|
|
@@ -10810,7 +10902,7 @@ function renderImpactMarkdown(r2) {
|
|
|
10810
10902
|
L.push("## Impact Signals", "");
|
|
10811
10903
|
const s = r2.impact_signals;
|
|
10812
10904
|
L.push(`- Ambiguity reduction: ${s.ambiguity_reduction}`);
|
|
10813
|
-
L.push(`- Drift prevention: ${s.drift_prevention}`);
|
|
10905
|
+
L.push(`- Drift prevention evidence: ${s.drift_prevention}`);
|
|
10814
10906
|
L.push(`- Onboarding support: ${s.onboarding_support}`);
|
|
10815
10907
|
L.push(`- Delivery traceability: ${s.delivery_traceability}`);
|
|
10816
10908
|
L.push(`- AI context readiness: ${s.ai_context_readiness}`);
|
|
@@ -10829,12 +10921,7 @@ function renderImpactMarkdown(r2) {
|
|
|
10829
10921
|
L.push("");
|
|
10830
10922
|
};
|
|
10831
10923
|
gapSection("Work Items missing source", gp.missing_source);
|
|
10832
|
-
gapSection("Work Items missing initiative", gp.missing_initiative);
|
|
10833
10924
|
gapSection("Work Items missing code ownership", gp.missing_code_ownership);
|
|
10834
|
-
gapSection("Work Items missing knowledge level", gp.missing_knowledge_level);
|
|
10835
|
-
gapSection("Work Items missing acceptance criteria", gp.missing_acceptance_criteria);
|
|
10836
|
-
gapSection("Work Items missing Definition of Done", gp.missing_definition_of_done);
|
|
10837
|
-
gapSection("Work Items missing validation", gp.missing_validation);
|
|
10838
10925
|
if (gp.broad_ownership_globs.length > 0) {
|
|
10839
10926
|
L.push("### Broad ownership globs", "");
|
|
10840
10927
|
for (const b2 of gp.broad_ownership_globs) {
|
|
@@ -10845,7 +10932,7 @@ function renderImpactMarkdown(r2) {
|
|
|
10845
10932
|
L.push("");
|
|
10846
10933
|
}
|
|
10847
10934
|
if (gp.ownership_overlaps.length > 0) {
|
|
10848
|
-
L.push("###
|
|
10935
|
+
L.push("### Shared code-path history (review only)", "");
|
|
10849
10936
|
for (const o2 of gp.ownership_overlaps) {
|
|
10850
10937
|
L.push(`- \`${o2.code_path}\``);
|
|
10851
10938
|
L.push(` - Owned by: ${o2.work_items.join(", ")}`);
|
|
@@ -10853,10 +10940,26 @@ function renderImpactMarkdown(r2) {
|
|
|
10853
10940
|
}
|
|
10854
10941
|
L.push("");
|
|
10855
10942
|
}
|
|
10856
|
-
const anyGaps = gp.missing_source.length + gp.
|
|
10943
|
+
const anyGaps = gp.missing_source.length + gp.missing_code_ownership.length + gp.broad_ownership_globs.length;
|
|
10857
10944
|
if (anyGaps === 0) {
|
|
10858
10945
|
L.push("No actionable knowledge gaps detected. \u{1F389}", "");
|
|
10859
10946
|
}
|
|
10947
|
+
const historical = [
|
|
10948
|
+
["knowledge level", gp.missing_knowledge_level],
|
|
10949
|
+
["Acceptance Criteria", gp.missing_acceptance_criteria],
|
|
10950
|
+
["Definition of Done", gp.missing_definition_of_done],
|
|
10951
|
+
["validation section", gp.missing_validation]
|
|
10952
|
+
];
|
|
10953
|
+
L.push("## Historical Documentation Completeness", "");
|
|
10954
|
+
for (const [label, items] of historical) {
|
|
10955
|
+
const completed = items.filter((item) => item.status === "completed");
|
|
10956
|
+
if (completed.length > 0) L.push(`- Completed Work Items without detected ${label}: ${completed.length}`);
|
|
10957
|
+
}
|
|
10958
|
+
if (!historical.some(([, items]) => items.some((item) => item.status === "completed"))) {
|
|
10959
|
+
L.push("No historical documentation completeness gaps were detected.");
|
|
10960
|
+
}
|
|
10961
|
+
L.push("- This is an inventory for review, not a recommendation to rewrite historical Work Items merely to increase a metric.");
|
|
10962
|
+
L.push("");
|
|
10860
10963
|
if (r2.score_breakdown) {
|
|
10861
10964
|
L.push("## Score Breakdown", "");
|
|
10862
10965
|
const b2 = r2.score_breakdown;
|
|
@@ -11006,6 +11109,16 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
11006
11109
|
} else {
|
|
11007
11110
|
actions.push("Use drift history to calibrate `rework_hours_avoided_per_resolved_drift`.");
|
|
11008
11111
|
}
|
|
11112
|
+
const calibrationOpportunities = !a2.calibrated_at ? {
|
|
11113
|
+
available: true,
|
|
11114
|
+
reason: source3 === "default" ? "Default assumptions are uncalibrated estimates." : "Project assumptions are not marked as calibrated.",
|
|
11115
|
+
opportunities: [
|
|
11116
|
+
{ area: "Context preparation", suggested_evidence: "Compare preparation effort on comparable tasks before and after using the knowledge artifacts." },
|
|
11117
|
+
{ area: "Review effort", suggested_evidence: "Record a baseline for comparable reviews with similar ownership coverage." },
|
|
11118
|
+
{ area: "Clarification reduction", suggested_evidence: "Estimate effort spent resolving repeated scope or context questions." },
|
|
11119
|
+
{ area: "Architecture discovery", suggested_evidence: "Compare discovery time for similar dependency or relationship analyses." }
|
|
11120
|
+
]
|
|
11121
|
+
} : { available: false, reason: `Assumptions are marked as calibrated on ${a2.calibrated_at}.`, opportunities: [] };
|
|
11009
11122
|
return {
|
|
11010
11123
|
generated_at: now.toISOString(),
|
|
11011
11124
|
project: impact.project,
|
|
@@ -11057,6 +11170,7 @@ function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
11057
11170
|
},
|
|
11058
11171
|
total: { estimated_hours_saved: observedHours, estimated_value: observedValue, currency: a2.currency },
|
|
11059
11172
|
confidence: { level, reasons },
|
|
11173
|
+
calibration_opportunities: calibrationOpportunities,
|
|
11060
11174
|
suggested_actions: actions
|
|
11061
11175
|
};
|
|
11062
11176
|
}
|
|
@@ -11138,6 +11252,17 @@ function renderSavingsMarkdown(r2) {
|
|
|
11138
11252
|
L.push(`Confidence: ${r2.confidence.level}`, "", "Reason:");
|
|
11139
11253
|
for (const reason of r2.confidence.reasons) L.push(`- ${reason}`);
|
|
11140
11254
|
L.push("");
|
|
11255
|
+
L.push("## Calibration Opportunities", "");
|
|
11256
|
+
if (!r2.calibration_opportunities.available) {
|
|
11257
|
+
L.push(`- ${r2.calibration_opportunities.reason}`);
|
|
11258
|
+
} else {
|
|
11259
|
+
L.push(`- ${r2.calibration_opportunities.reason}`);
|
|
11260
|
+
L.push("- Use external, aggregate project observations; do not collect or store personal data.");
|
|
11261
|
+
for (const opportunity of r2.calibration_opportunities.opportunities) {
|
|
11262
|
+
L.push(`- ${opportunity.area}: ${opportunity.suggested_evidence}`);
|
|
11263
|
+
}
|
|
11264
|
+
}
|
|
11265
|
+
L.push("");
|
|
11141
11266
|
L.push("## Suggested Actions", "");
|
|
11142
11267
|
r2.suggested_actions.forEach((act, i) => L.push(`${i + 1}. ${act}`));
|
|
11143
11268
|
L.push("");
|
|
@@ -11158,10 +11283,24 @@ function buildDriftReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11158
11283
|
const deprecationFindings = plans.flatMap((plan) => getDeprecationPlan(dir, plan.id)?.targets.flatMap(validateTarget) ?? []);
|
|
11159
11284
|
const config = loadConfig(dir);
|
|
11160
11285
|
const topology = config ? buildGraph(dir, config, { scope: "all" }, now) : null;
|
|
11161
|
-
const
|
|
11286
|
+
const graphHints = topology ? buildGraphHints(dir, topology, now) : null;
|
|
11287
|
+
const topologyHints = graphHints?.summary.hints ?? null;
|
|
11288
|
+
const currentConsistencyFindings = [
|
|
11289
|
+
...graphHints?.hints.map(graphHintFinding) ?? [],
|
|
11290
|
+
...deprecationFindings.map((finding, index) => ({
|
|
11291
|
+
id: `deprecation-target:${index + 1}`,
|
|
11292
|
+
category: "consistency",
|
|
11293
|
+
source: "deprecation-target-validation",
|
|
11294
|
+
affected_artifacts: [],
|
|
11295
|
+
priority: "medium",
|
|
11296
|
+
evidence: String(finding),
|
|
11297
|
+
explanation: "A deterministic deprecation target consistency check reported a finding.",
|
|
11298
|
+
suggested_action: "Review the affected deprecation target and its declared lifecycle evidence."
|
|
11299
|
+
}))
|
|
11300
|
+
];
|
|
11162
11301
|
const actions = [];
|
|
11163
11302
|
if (!h2.available) {
|
|
11164
|
-
actions.push("
|
|
11303
|
+
actions.push("Start recording Guard runs at relevant delivery checkpoints with `kaddo guard --record`; one run creates an observation, while multiple comparable runs are needed for a historical trend.");
|
|
11165
11304
|
} else {
|
|
11166
11305
|
if (h2.open > 0) actions.push("Review open drift warnings and update related Work Items or ownership.");
|
|
11167
11306
|
actions.push("Run `kaddo guard --record` before important commits.");
|
|
@@ -11177,6 +11316,11 @@ function buildDriftReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11177
11316
|
{ 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
11317
|
{ 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
11318
|
],
|
|
11319
|
+
current_consistency_findings: currentConsistencyFindings,
|
|
11320
|
+
structural_consistency: {
|
|
11321
|
+
deprecation_target_checks: plans.length === 0 ? "not_applicable" : deprecationFindings.length === 0 ? "passed" : "findings_detected",
|
|
11322
|
+
note: plans.length === 0 ? "No deprecation plans exist in this project." : "Target consistency checks validate declared structural state only; they do not verify operational removal from infrastructure, production or external consumers."
|
|
11323
|
+
},
|
|
11180
11324
|
guard_history: {
|
|
11181
11325
|
available: h2.available,
|
|
11182
11326
|
total_runs: h2.total_runs,
|
|
@@ -11202,10 +11346,31 @@ function renderDriftMarkdown(r2) {
|
|
|
11202
11346
|
L.push("## Consistency Evidence", "");
|
|
11203
11347
|
for (const item of r2.consistency_evidence) L.push(`- ${item.label}: ${item.classification} \u2014 ${item.reason}`);
|
|
11204
11348
|
L.push("");
|
|
11349
|
+
L.push("## Current Consistency Findings", "");
|
|
11350
|
+
if (r2.current_consistency_findings.length === 0) {
|
|
11351
|
+
L.push("No findings detected by the executed consistency checks.");
|
|
11352
|
+
L.push("");
|
|
11353
|
+
}
|
|
11354
|
+
if (r2.current_consistency_findings.length > 0) {
|
|
11355
|
+
const cell = (value) => value.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
11356
|
+
L.push("| Priority | Source | Affected | Finding | Suggested action |");
|
|
11357
|
+
L.push("|---|---|---|---|---|");
|
|
11358
|
+
for (const finding of r2.current_consistency_findings) {
|
|
11359
|
+
L.push(`| ${finding.priority} | ${cell(finding.source)} | ${cell(finding.affected_artifacts.join(", ") || "not available")} | ${cell(finding.explanation)} Evidence: ${cell(finding.evidence)} | ${cell(finding.suggested_action)} |`);
|
|
11360
|
+
}
|
|
11361
|
+
L.push("");
|
|
11362
|
+
}
|
|
11363
|
+
L.push("## Structural Consistency Boundaries", "");
|
|
11364
|
+
L.push(`- Deprecation target checks: ${r2.structural_consistency.deprecation_target_checks}`);
|
|
11365
|
+
L.push(`- Note: ${r2.structural_consistency.note}`);
|
|
11366
|
+
L.push("");
|
|
11205
11367
|
if (!r2.guard_history.available) {
|
|
11206
11368
|
L.push("## Guard History", "");
|
|
11207
11369
|
L.push("No guard history recorded yet.");
|
|
11208
11370
|
L.push("");
|
|
11371
|
+
L.push("## Historical Drift Trend", "");
|
|
11372
|
+
L.push("Not available \u2014 no recorded Guard runs.");
|
|
11373
|
+
L.push("");
|
|
11209
11374
|
L.push("Run `kaddo guard --record` to start recording drift evidence.");
|
|
11210
11375
|
L.push("");
|
|
11211
11376
|
L.push("## Suggested Actions", "");
|
|
@@ -11270,6 +11435,8 @@ function serializeDriftJson(r2) {
|
|
|
11270
11435
|
project: r2.project,
|
|
11271
11436
|
project_context: r2.project_context,
|
|
11272
11437
|
consistency_evidence: r2.consistency_evidence,
|
|
11438
|
+
current_consistency_findings: r2.current_consistency_findings,
|
|
11439
|
+
structural_consistency: r2.structural_consistency,
|
|
11273
11440
|
guard_history: r2.guard_history,
|
|
11274
11441
|
drift_warnings: r2.drift_warnings,
|
|
11275
11442
|
hotspots: r2.hotspots,
|
package/package.json
CHANGED